Is there any possibility of locking canvas element? In my app I draw complex images and sometimes I use bitmaps. Using bitmaps with canvas isn't quite comfortable - all the drawing that should be placed on canvas after bitmap is placed in bitmaps .onload.
It would be a lot easier if I could lock and unlock canvas so it couldn't be updated for some time.
AFAIK there is no built-in function for lock/unlock. Do you know any simple way of implementing it?
I don't know whether this is bulletproof or not, but you could temporarily make .stroke etc. noops: http://jsfiddle.net/eGjak/247/.
var toLock = "stroke fill".split(" ");
function lock() {
$.each(toLock, function(i, name) { // or another way to loop the array
ctx[name] = function() {};
});
}
function unlock() {
$.each(toLock, function(i, name) {
ctx[name] = ctx.constructor.prototype[name];
});
}
$("canvas").attr("disable","False");
// bitmap .onload...
$("canvas").attr("disable","True");
Or
bitmap.onload = function(){
var canvas = window.getCanvas();
});
Related
I'm working on a project in game development in Javascript and HTML5 canvas. I have this common code I use for loading sprites:
var sprite = new Image();
sprite.src = "sprite.png";
I was wondering if there was a simpler way to do this, which I first thought by function, but not sure how I should do so. I would think to do so like this:
function loadSprite(src) {
this.src = src;
}
var loadSprite(sprite.png);
However I don't think this is the right way to do it. Could someone correct my code and/or give a simpler way of loading an image like this? (I am also using a ctx.drawImage(..., sprite) in order to change coordinates on the canvas so it needs an x,y,width,and height parameters in one way or another)
Why not use as below:
function loadSprite(src) {
var sprite = new Image();
sprite.src = src;
return sprite
}
var _local_var = loadSprite('sprite.png');
Following instructions from this question, I have a div which is being cloned that has a p5 canvas inside it. When it is cloned the canvas is not responsive to mouse coordinates.
How do I make the p5 canvas still active after cloning?
$(document).ready(function() {
$(".showcanvas").click(function() {
// Find the original canvas element
var origCanvas = $(".canvas").first().find('canvas')[0];
// Clone the div wrapper
var clonedDiv = $(".canvas").first().clone(true);
// Find the cloned canvas
var clonedCanvas = clonedDiv.find('canvas');
// Update the ID. (IDs should be unique in the document)
clonedCanvas.prop('id', 'defaultCanvas' + $(".canvas").length)
// Draw the original canvas to the cloned canvas
clonedCanvas[0].getContext('2d').drawImage(origCanvas, 0, 0);
// Append the cloned elements
clonedDiv.appendTo("article").fadeIn('1200');
});
});
https://jsfiddle.net/vanderhurk/12fxj48h/28/
I was going to comment on your previous question about this. The approach you're taking of cloning the canvas element and then drawing the old canvas into the new canvas is going to have exactly this problem: the new canvas is not going to change as the old canvas changes. This might work if you just want to take a snapshot (although there are easier ways to do this), but my guess is it's not what you're looking for.
Instead of cloning the element and dealing with JQuery, I suggest you look into using instance mode from the P5.js library. You can read more about instance mode here, but basically it allows you to package your P5.js code in a self-contained object.
Here's a simple example:
var s = function( sketch ) {
var x = 100;
var y = 100;
sketch.setup = function() {
sketch.createCanvas(200, 200);
};
sketch.draw = function() {
sketch.background(0);
sketch.fill(255);
sketch.rect(x,y,50,50);
};
};
var myp5 = new p5(s);
If I were you, I would encapsulate the creation of an instance-mode P5.js sketch into a function or class. Then whenever I want to create a new copy of the sketch, I'd call that function or class. You could also store any shared state in global variables that both sketches access.
I have a project using HTML5 Canvas (createjs) and I've had issues with spikes on text strokes, meaning I have to set the miterlimit of the 2d context. However, the parent (which I have no control over) scales the canvas when the window is resized, which obviously resets the canvas context.
Now, I want to avoid putting an onresize event inside the client - my first thought is just to use the createjs Ticker thus:
createjs.Ticker.addEventListener("tick", handleTick);
function handleTick(event) {
var ctx = canvas.getContext('2d');
ctx.miterLimit = 2;
}
Though this seems a little wasteful, is there a more efficient way of doing this, without using DOM events?
Your approach might work, but its definitely a hack, since you can't expect that context properties will be maintained, or that they won't be applied in the wrong place.
If you do want to patch the display list to update the context, you can use the "drawstart" event, which fires before the display list is drawn:
stage.on("drawstart", function(e) {
var ctx = stage.canvas.getContext("2d");
ctx.miterLimit = 2;
});
However if you want a better approach that is instance-specific, you can extend the Text class to append any context properties you want. Here is a quick example where miterLimit is stored and applied any time the text is drawn. In this example, you can create multiple instances and set different miter limits on them. Note that you might want to also support other properties such as lineJoin.
http://jsfiddle.net/cr4hmgqp/2/
(function() {
"use strict"
function MiterText(text, font, color, miterLimit) {
this.Text_constructor(text,font,color);
this.miterLimit = miterLimit;
};
var p = createjs.extend(MiterText, createjs.Text);
p.draw = function(ctx, ignoreCache) {
ctx.miterLimit = this.miterLimit;
if (this.Text_draw(ctx, ignoreCache)) { return true; }
return true;
};
p.clone = function() {
return this._cloneProps(new MiterText(this.text, this.font, this.color, this.miterLimit));
};
createjs.MiterText = createjs.promote(MiterText, "Text");
}());
Note that this issue should hopefully be fixed in the next version of EaselJS. Here is the tracked issue: https://github.com/CreateJS/EaselJS/issues/781
Cheers.
I have some code that renders specific DOM elements to canvas, sort of like taking a screenshot. (It's custom code built for a very particular DOM structure as part of a graphics editing game, not a general library like rasterHTML.js)
The code flow is pretty procedural:
get some DOM elements of class A and draw them to canvas
get some DOM elements of class B and draw them to canvas
The trouble is that step 1 is very intensive compared to step 2, and doesn't finish drawing before step 2, screwing up the layers (in reality I have several canvases doing several things at once, and a canvas is unfortunately resized before all the drawing is completed). I've tried to replicate this in this fiddle: https://jsfiddle.net/1hucuLg9/
In pseudocode:
context.drawComplexSVG(); // slow
context.drawSimpleImage(); //fast
//canvas now has an SVG drawn on top of an image, not underneath.
I've seen a lot of setTimeout examples in an attempt to get one function to execute after another, but to me this seems to be a bit of a hack ... ideally I don't want to delay execution, just execute everything in strict order. I've also seen the idea of postMessage floated to achieve this but I've no idea how you pass messages to yourself. What's the correct way to ensure a function/line is fully executed (or in my case, the canvas is fully updated - is it the same thing?) before proceeding?
"getting some DOM elements" should be synchronous and do not require any complex code to handle sequencing draw operations.
The problem you are facing in your fiddle is that you are dynamically loading some images to draw - and for those, you need to wait, which makes the operation asynchronous.
Promises are here for your rescue, but you'll have to use them correctly. Just calling resolve right away like you did in your own answer will ensure some asynchrony, but that's not less fragile than a setTimeout approach. Instead, you should always create the promise at the heart of the asynchrony, in your case the image loading:
function loadImage(src) {
return new Promise(resolve, reject) {
var img = new Image();
img.onload = function(){ resolve(img); };
img.onerror = reject;
img.src = src;
});
}
so that you can use it in your canvas drawing code:
function drawSwatches(currentSwatch) {
var data = …;
var url = 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(data);
return loadImage(url).then(function(img) {
ctx.drawImage(img, 0, 0, vw, vh);
});
}
and then chain these properly:
var swatches = Array.from(document.getElementsByClassName("swatch"));
swatches.reduce(function(promise, swatch) {
return promise.then(function() {
return drawSwatches(swatch);
});
}, Promise.resolve()).then(function() {
var otherObjects = document.getElementsByClassName("otherObjects");
for (var i=0; i<otherObjects.length; i++) {
drawOtherObjects(otherObjects[i], 0, 0, 100, 100);
}
});
Well, it seems that this will get the job done:
var promise = new Promise(function(resolve){
context.drawComplexSVG();
resolve();
}
promise.then(function(){
context.drawSimpleImage();
}
I am creating a image in a canvas and saving the image. I found a very nice plugin here.
Code for the image saving:
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var imageObj = new Image();
imageObj.src = imageURI;
imageObj.onload = function() {
contentW = $("#content").width();
canvas.width = 400;
canvas.height = 600;
context.drawImage(imageObj, 0, 0,canvas.width,canvas.height);
//the plugin
setTimeout(function(){
window.savephotoplugin(canvas,"image/png",device.version,function(val){
//returns you the saved path in val
alert("Photo Saved: " + val);
});
},3000)
}
The plugin works very nice only problem is that it is done before the canvas is even drawn. So I put a setTimeout to avoid it, however is there a way to detect when canvas is done and call the function after it. Tried jquery .change() didn't work.
If anyone finds this code useful feel free to use and the plugin is very nice :)
As you can see drawImage doesn't accept any callbacks. Also canvas doesn't define any events about its drawing process. So you choice of timeout is correct. Only one thing you could possibly improve. Use setTimeout(..., 0) instead of setTimeout(..., 3000). More details about this trick here