What is best practice? Should I make a separate canvas tag for each image in html5, or is it just as functional to put multiple images in one canvas, and how would I do that.
My Code so far:
var canvas = document.getElementById("e");
var context = canvas.getContext("2d");
var cat = new Image();
cat.src = "images/cartoonPaul01.jpg";
cat.onload = function() {
context.drawImage(cat, 0, 0, 169, 207);
};
You can do this dynamically, if you're so inclined. muro from deviant art uses a few base canvases – temp, buffer, and background. Then you dynamically create and delete layers using a control on the side. It depends largely on what you want to do.
If you are just printing to the canvas, and you don't need to necessarily alter the images in a way that needs dynamism, you can draw them all to one canvas. If you need layering, use it.
Dynamic canvas and context creation:
// This is not best practice, just an example using globals.
var layers = [];
var lyrCtx = [];
createLayer = function() {
// Create the canvas.
layers.push(document.createElement('canvas'));
layers[layers.length - 1].setAttribute('id', 'layer'+layers.length-1);
// Create the context. Provided you do it in order, lyrCtx[i] should correlate to layers[i]
lyrCtx.push(layers[layers.length - 1].getContext("2d"));
// Handle context settings here.
}
You should get the balance. I usually use 4-6 canvas layers in my app, and draw logical part on them.
Related
I made a double pendulum with canvas.
Here is the result: https://jsfiddle.net/zndo9vh4/
As you guys can see a trace is drawn everytime the second part of the pendulum moves, and my way of doing that is by appending each coordinate to a "trace" array.
var trace = []
trace.push([x2,y2]);
And then I draw the trace by joining each coordinate with the last one:
for (let i = 1; i < trace.length; i++) {
c.moveTo(trace[i][0], trace[i][1])
c.lineTo(trace[i-1][0], trace[i-1][1])
}
I want to improve it. What i've tried so far is only adding coordinates that aren't already in the array, but it's not a big improvent because the lines are drawn every loop
var trace = []
if(trace.includes([x2, y2]) != true){
trace.push([x2,y2]);
}
The way I think could be a good improvement is by having 2 canvas (I don't know if its possible) and then draw each point but only in that canvas so I doesnt have to be redrawn. But I dont know how to implement that.
Thanks in advice
Your improvement idea is great. You can indeed have two canvases!
There are two ways to go about it.
Offscreen canvas
Using what's called an offscreen canvas (a canvas that is created in JavaScript but not added to the DOM), you can draw all the points onto it and then using drawImage (which can accept a canvas element) pass the canvas to the main context.
var offscreenCanvas = document.createElement('canvas');
var offscreenC = offscreenCanvas.getContext('2d');
offscreenCanvas.width = canvas.width;
offscreenCanvas.height = canvas.height;
// in animate function, draw points onto the offscreen canvas instead
// of the regular canvas as they are added
if(trace.includes([x2, y2]) != true){
trace.push([x2,y2]);
var i = trace.length-1;
if (i > 1) {
offscreenC.strokeStyle = 'white'
offscreenC.beginPath();
offscreenC.moveTo(trace[i][0], trace[i][1])
offscreenC.lineTo(trace[i-1][0], trace[i-1][1])
offscreenC.stroke();
}
}
c.drawImage(offscreenCanvas, 0, 0);
Layered Canvases
One of the downsides to the offscreen canvas approach is that you have to draw it to the main canvas every frame. You can further improve the approach by layering two canvases on top of one another, where the top one is just the pendulum and the bottom one the trace.
This way, you never have to redraw the offscreen canvas onto the main canvas, and save yourself some rendering time.
Updated jsfiddle
I'm loading a 3D asset into a Away3D scene and I'd like to move the position of the bones in code.
The asset loading all goes well, I grab a pointer to the Mesh and Skeleton while loading:
private function onAssetComplete(evt:AssetEvent):void
{
if(evt.asset.assetType == AssetType.SKELETON){
_skeleton = evt.asset as Skeleton;
} else if (evt.asset.assetType == AssetType.MESH) {
_mesh = evt.asset as Mesh;
}
}
After the asset(s) have finished loading, I have a valid Skeleton and Mesh instance, the model is also visible in my scene. The next thing I tried is the following.
// create a matrix with the desired joint (bone) position
var pos:Matrix3D = new Matrix3D();
pos.position = new Vector3D(60, 0, 0);
pos.invert();
// get the joint I'd like to modifiy. The bone is named "left"
var joint:SkeletonJoint = _skeleton.jointFromName("left");
// assign joint position
joint.inverseBindPose = pos.rawData;
This code runs without error, but the new position isn't being applied to the visible geometry, eg. the position of the bone doesn't change at all.
Is there an additional step I'm missing here? Do I have to re-assign the skeleton to the Mesh somehow? Or do I have to explicitly tell the mesh that the bone positions have changed?
This might not be the best way to solve this, but here's what I figured out:
Away3D only applies joint transformations to the geometry when an animation is present. In order to apply your transforms, your geometry must have an animation or you'll have to create an animation in code. Here's how you do that (preferably in your LoaderEvent.RESOURCE_COMPLETE handler method:
// create a new pose for the skeleton
var rootPose:SkeletonPose = new SkeletonPose();
// add all the joints to the pose
// the _skeleton member is being assigned during the loading phase where you
// look for AssetType.SKELETON inside a AssetEvent.ASSET_COMPLETE listener
for each(var joint:SkeletonJoint in _skeleton.joints){
var m:Matrix3D = new Matrix3D(joint.inverseBindPose);
m.invert();
var p:JointPose = new JointPose();
p.translation = m.transformVector(p.translation);
p.orientation.fromMatrix(m);
rootPose.jointPoses.push(p);
}
// create idle animation clip by adding the root pose twice
var clip:SkeletonClipNode = new SkeletonClipNode();
clip.addFrame(rootPose, 1000);
clip.addFrame(rootPose, 1000);
clip.name = "idle";
// build animation set
var animSet:SkeletonAnimationSet = new SkeletonAnimationSet(3);
animSet.addAnimation(clip);
// setup animator with set and skeleton
var animator:SkeletonAnimator = new SkeletonAnimator(animSet, _skeleton);
// assign the newly created animator to your Mesh.
// This example assumes that you grabbed the pointer to _myMesh during the
// asset loading stage (by looking for AssetType.MESH)
_myMesh.animator = animator;
// run the animation
animator.play("idle");
// it's best to keep a member that points to your pose for
// further modification
_myPose = rootPose;
After that initialization step, you can modify your joint poses dynamically (you alter the position by modifying the translation property and the rotation by altering the orientation property). Example:
_myPose.jointPoses[2].translation.x = 100;
If you don't know the indices of your joints and rather address bones by name, this should work:
var jointIndex:int = _skeleton.jointIndexFromName("myBoneName");
_myPose.jointPoses[jointIndex].translation.y = 10;
If you use the name-lookup frequently (say every frame) and you have a lot of bones in your model, it's advisable to build a Dictionary where you can look up bone indices by name. The reason for this is that the implementation of jointIndexFromName performs a linear search through all joints which is wasteful if you do this multiple times.
I am using
var texture1 = THREE.ImageUtils.loadTexture("image1.jpg");
to map this texture to a material.
My query is that can I load texture by css as I want to load images by css sprites. I need to give background position with every texture which is not possible if I load textures via html.
I need to use background : url() property.
Please let me know how it can be done
The thing to note here is that your entire Three.js scene is being rendered on an HTML Canvas. That said you now exist in a purely GDI based environment. The only solution I can see for you now is to remap your materials texture calling an update pointing to a new "2d" canvas context. Something like this:
var spriteCanvas = document.createElement( "canvas" );
var spriteContext = spriteCanvas.getContext( "2d" );
var spriteImage = document.getElementById( "hidden_spriteDiv_with_background_url" );
spriteContext.drawImage( spriteImage, 0, 0 );
var texture1 = new THREE.Texture( spriteContext );
texture1.needsUpdate = true;
Then assigning the new Texture map "texture1" to your material with applicable updates called. Hope that at least gets you started.
Hi i am building a windows store app with html5 and javascript in my app i am trying to implement an eraser tool but this is problematic because if the user moves an image or another layer to where they've previously erased, they see the white drawing where they erased.
i have been trying to do the eraser tool from different ways for example i have changed the default globalCompositeOperation to "destination-out" like this code
//Here is the error.
if (clickTool[j] == "eraser") {
ctx.globalCompositeOperation = 'destination-out';
ctx.fillStyle = 'rgba(255,0,0,0.5);';
ctx.strokeStyle = 'rgba(255,0,0,0.5);';
}
else {
ctx.globalCompositeOperation = "source-over";
ctx.strokeStyle = clickColor[j];
}
but unfortunately it doesn´t work for me. i have uploaded all my code to this link:
My code
Please i would like to somebody could help me.
Thanks and i'm sorry for my speech , i'm mexican.
Use multiple layers. Have one canvas for the background image and another for the drawing; that why you never erase any of the background image.
If you need to, you can have multiple layers as they don't generally impact performance.
And of course if you can combine layers, say the last drawn squiggle to the background layer, if you deem a drawing to be "permanent".
Maintain a array of mid points. Use the globalCompositeOperation as 'destination-out' first and 'source-over' later to make a transparent eraser trail .
Following is the code that you need to use with a mouse move function
var handleMouseMove = function (event) {
midPt = new createjs.Point(oldPt.x + stage.mouseX>>1, oldPt.y+stage.mouseY>>1);
if(curTool.type=="eraser"){
var tempcanvas = document.getElementById('drawcanvas');
var tempctx=tempcanvas.getContext("2d");
tempctx.beginPath();
tempctx.globalCompositeOperation = "destination-out";
tempctx.arc(midPt.x, midPt.y, 20, 0, Math.PI * 2, false);
tempctx.fill();
tempctx.closePath();
tempctx.globalCompositeOperation = "source-over";
drawingCanvas.graphics.clear();
// keep updating the array for points
arrMidPtx.push(midPt.x);
arrMidPty.push(midPt.y);
stage.addChild(drawingCanvas);
stage.update();
}
};
I use this code to make a eraser that behaves like pen and fills up transparent color instead of white
Is there a putpixel functionality in html5. I have a canvas and want to draw on the canvas using mouse. How should I wright the javascript function for that. How can can I take the coordinate value on mouseclick and change the pixel value as soon as the user clicks.
From http://net.tutsplus.com/tutorials/javascript-ajax/canvas-from-scratch-pixel-manipulation/ section "Putting this into practice" using jquery:
$(canvas).click(function(e) {
var canvasOffset = $(canvas).offset();
var canvasX = Math.floor(e.pageX-canvasOffset.left);
var canvasY = Math.floor(e.pageY-canvasOffset.top);
// do here whatever you want
});
Read the article for a detailed explanation.
If you have a intermediate knowledge of Javascript try reading a tutorial like this:
http://www.williammalone.com/articles/create-html5-canvas-javascript-drawing-app/