So I have a form, and in this form a user can upload a picture. As an alternative, I want them to be able to take a picture and upload that instead.
Now I figure that when a user takes a picture with some sort of flash webcam interface, that picture needs to be stored in some sort of intermediary memory while the user completes his/her form.
Is there a way around this? What's the best solution?
I figure there are only really two options.
Either (a) I hold the temporary photo and then accept it again once the form is submitted or (b) the user takes a picture, it's downloaded onto their local machine, and they have to choose it as a file to be submitted again.
Neither of these solutions are really satisfactory to me, so I wanted to know if there's any better way to do this.
Edit: As an added bonus, although it's not really relevant, I'm making a Rails app with Paperclip for file attachments. Also, I prefer to work with jQuery...
Also just general opinions from experienced UI web developers would be nice..
I'd used this: http://www.xarg.org/project/jquery-webcam-plugin/ in my rails application for capturing an image from a webcam. You can download this jQuery-webcam here: https://github.com/infusion/jQuery-webcam
Here is a roughly done implementation, sorry for the messy code:
class PicturesController < ApplicationController
require 'base64'
def capture
# do something
render :layout => "webcam"
end
def save_image
image = params[:capture][:image]
File.open("#{Rails.root}/public/path_you_want_to_image/image_name.png", 'wb') do |f|
f.write(Base64.decode64(image))
end
# Or use paperclip to save image for a model instead!!
end
end
views/layouts/webcam.html.erb:
<!DOCTYPE html>
<html>
<head>
<title>Application Name</title>
<%= stylesheet_link_tag :all %>
<%= javascript_include_tag :defaults %>
<%= javascript_include_tag "http://www.google-analytics.com/ga.js"%>
<%= javascript_include_tag "http://code.jquery.com/jquery-1.4.2.min.js"%>
<%= javascript_include_tag "jquery.webcam"%>
<script>
!window.jQuery && document.write('<script src="jquery-1.4.3.min.js"><\/script>');
</script>
<%= csrf_meta_tag %>
</head>
<body>
<%= yield %>
</body>
</html>
views/pictures/capture.html.erb:
<div id="webcam">
<p>Capture image here!</p>
</div>
<%= form_for(save_image_pictures_path, :method => "post", :remote => true) do |f|%>
<%= hidden_field(:item, :sku)%>
<div id="capture_images"><input id="capture_image" type="hidden" value="" name="capture[image]"></div>
<%= submit_tag "Capture Image", :onClick=>"javascript:capture_image();"%>
<% end %>
<%= link_to "Back", root_path %>
<p>
<canvas id="canvas" width="320" height="240"></canvas>
</p>
<script type="text/javascript">
var pos = 0;
var ctx = null;
var cam = null;
var image = null;
var filter_on = false;
var filter_id = 0;
function changeFilter() {
if (filter_on) {
filter_id = (filter_id + 1) & 7;
}
}
function capture_image(){
webcam.capture();
changeFilter();
void(0);
var canvas = document.getElementById('canvas')
var context = canvas.getContext("2d");
var img = canvas.toDataURL("image/png");
var item_image = img.replace(/^data:image\/(png|jpg);base64,/, "") ;
document.getElementById('capture_images').innerHTML="<input id=\"capture_image\" type=\"hidden\" value=\""+item_image+"\" name=\"capture[image]\">";
}
function toggleFilter(obj) {
if (filter_on =!filter_on) {
obj.parentNode.style.borderColor = "#c00";
} else {
obj.parentNode.style.borderColor = "#333";
}
}
jQuery("#webcam").webcam({
width: 320,
height: 240,
mode: "callback",
swffile: "/javascripts/jscam_canvas_only.swf",
onTick: function(remain) {
if (0 == remain) {
jQuery("#status").text("Cheese!");
} else {
jQuery("#status").text(remain + " seconds remaining...");
}
},
onSave: function(data) {
var col = data.split(";");
var img = image;
if (false == filter_on) {
for(var i = 0; i < 320; i++) {
var tmp = parseInt(col[i]);
img.data[pos + 0] = (tmp >> 16) & 0xff;
img.data[pos + 1] = (tmp >> 8) & 0xff;
img.data[pos + 2] = tmp & 0xff;
img.data[pos + 3] = 0xff;
pos+= 4;
}
} else {
var id = filter_id;
var r,g,b;
var r1 = Math.floor(Math.random() * 255);
var r2 = Math.floor(Math.random() * 255);
var r3 = Math.floor(Math.random() * 255);
for(var i = 0; i < 320; i++) {
var tmp = parseInt(col[i]);
/* Copied some xcolor methods here to be faster than calling all methods inside of xcolor and to not serve complete library with every req */
if (id == 0) {
r = (tmp >> 16) & 0xff;
g = 0xff;
b = 0xff;
} else if (id == 1) {
r = 0xff;
g = (tmp >> 8) & 0xff;
b = 0xff;
} else if (id == 2) {
r = 0xff;
g = 0xff;
b = tmp & 0xff;
} else if (id == 3) {
r = 0xff ^ ((tmp >> 16) & 0xff);
g = 0xff ^ ((tmp >> 8) & 0xff);
b = 0xff ^ (tmp & 0xff);
} else if (id == 4) {
r = (tmp >> 16) & 0xff;
g = (tmp >> 8) & 0xff;
b = tmp & 0xff;
var v = Math.min(Math.floor(.35 + 13 * (r + g + b) / 60), 255);
r = v;
g = v;
b = v;
} else if (id == 5) {
r = (tmp >> 16) & 0xff;
g = (tmp >> 8) & 0xff;
b = tmp & 0xff;
if ((r+= 32) < 0) r = 0;
if ((g+= 32) < 0) g = 0;
if ((b+= 32) < 0) b = 0;
} else if (id == 6) {
r = (tmp >> 16) & 0xff;
g = (tmp >> 8) & 0xff;
b = tmp & 0xff;
if ((r-= 32) < 0) r = 0;
if ((g-= 32) < 0) g = 0;
if ((b-= 32) < 0) b = 0;
} else if (id == 7) {
r = (tmp >> 16) & 0xff;
g = (tmp >> 8) & 0xff;
b = tmp & 0xff;
r = Math.floor(r / 255 * r1);
g = Math.floor(g / 255 * r2);
b = Math.floor(b / 255 * r3);
}
img.data[pos + 0] = r;
img.data[pos + 1] = g;
img.data[pos + 2] = b;
img.data[pos + 3] = 0xff;
pos+= 4;
}
}
if (pos >= 0x4B000) {
ctx.putImageData(img, 0, 0);
pos = 0;
}
},
onCapture: function () {
webcam.save('/product_capture');
jQuery("#flash").css("display", "block");
jQuery("#flash").fadeOut(100, function () {
jQuery("#flash").css("opacity", 1);
});
},
debug: function (type, string) {
jQuery("#status").html(type + ": " + string);
},
onLoad: function () {
var cams = webcam.getCameraList();
for(var i in cams) {
jQuery("#cams").append("<li>" + cams[i] + "</li>");
}
}
});
function getPageSize() {
var xScroll, yScroll;
if (window.innerHeight && window.scrollMaxY) {
xScroll = window.innerWidth + window.scrollMaxX;
yScroll = window.innerHeight + window.scrollMaxY;
} else if (document.body.scrollHeight > document.body.offsetHeight){ // all but Explorer Mac
xScroll = document.body.scrollWidth;
yScroll = document.body.scrollHeight;
} else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
xScroll = document.body.offsetWidth;
yScroll = document.body.offsetHeight;
}
var windowWidth, windowHeight;
if (self.innerHeight) { // all except Explorer
if(document.documentElement.clientWidth){
windowWidth = document.documentElement.clientWidth;
} else {
windowWidth = self.innerWidth;
}
windowHeight = self.innerHeight;
} else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode
windowWidth = document.documentElement.clientWidth;
windowHeight = document.documentElement.clientHeight;
} else if (document.body) { // other Explorers
windowWidth = document.body.clientWidth;
windowHeight = document.body.clientHeight;
}
// for small pages with total height less then height of the viewport
if(yScroll < windowHeight){
pageHeight = windowHeight;
} else {
pageHeight = yScroll;
}
// for small pages with total width less then width of the viewport
if(xScroll < windowWidth){
pageWidth = xScroll;
} else {
pageWidth = windowWidth;
}
return [pageWidth, pageHeight];
}
window.addEventListener("load", function() {
jQuery("body").append("<div id=\"flash\"></div>");
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
ctx = document.getElementById("canvas").getContext("2d");
ctx.clearRect(0, 0, 320, 240);
var img = new Image();
img.src = "/images/rails.png";
img.onload = function() {
ctx.drawImage(img, 129, 89);
}
image = ctx.getImageData(0, 0, 320, 240);
}
var pageSize = getPageSize();
jQuery("#flash").css({ height: pageSize[1] + "px" });
}, false);
window.addEventListener("resize", function() {
var pageSize = getPageSize();
jQuery("#flash").css({ height: pageSize[1] + "px" });
}, false);
</script>
Just make sure that, "jscam.swf" file referenced in jquery.webcam.js is loaded on page properly.
routes you can define in your routes.rb are:
resources :pictures do
collection do
get 'capture'
post 'save_image'
end
end
From here, you can then use this: https://github.com/blueimp/jQuery-File-Upload for uploading it using an Ajax form submission!!
Do let me know if you have any questions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With