Zooming on a HTML <canvas> - javascript

I'm creating a webpage that draws a trail along a path as the page is scrolled, using canvas to draw the trail and SVGs to determine the path coordinates. (Similar to this demo - https://tympanus.net/Development/StorytellingMap/)
I have a hidden canvas set up as a 'room' which contains the full map background, then draws from this to the main canvas (using a 'camera' to get the position). I've managed to get the background to zoom using drawImage to scale the selected area as follows:
ctx.drawImage(
image,
camera.position.x + ((canvas.width - (canvas.width / zoom)) / 2),
camera.position.y + ((canvas.height - (canvas.height / zoom)) / 2),
canvas.width / zoom,
canvas.height / zoom,
0,
0,
canvas.width,
canvas.height
);
The line is drawn by looping through an array of points like this:
ctx.beginPath();
for (var i = 0; i < points.length; i++) {
ctx.lineTo(
points[i].x - camera.position.x,
points[i].y - camera.position.y
);
}
ctx.stroke();
However I can't adapt the line to account for zoom - I can multiply all the coordinates by the zoom factor and the size appears correct, but I lose the position set by the camera, so it's not aligned to the background. Can anyone tell me how I can account for this?
I hope I've explained this well enough, I can try to upload the whole thing if anyone needs to see it in context!
Please let me know any questions, I'd really appreciate any advice or guidance.
Edit:
Link: http://staging.clicky.co.uk/map-scroll/ (Scroll to see the map update!)

Related

Create particles animation with three.js

I am new to three.js, I have created a simple particle app with three.js, but the flow of particles is moving from centre. I want move particles something like displayed in the attached screenshot (top right corner to bottom left corner).
working code is at https://jseditor.io/?key=ee98d51a9b4111eab74e00224d6bfcd5.
any reference will be very helpful. Thank you.
Your updateParticles function is only changing the particles' z position:
particle.position.z += mouseY * 0.1;
if(particle.position.z>1000) particle.position.z-=2000;
If you want to move them vertically and horizontally, you're going to have to update the x and y positions as well.
particle.position.x -= mouseY * 0.1;
particle.position.y -= mouseY * 0.1;
if(particle.position.x<-500) particle.position.x+=1000;
if(particle.position.y<-500) particle.position.y+=1000;
I don't know the size of your scene, so you might need to adjust the numbers accordingly.

HTML 5 Canvas - Rotate, Scale, Translate and drawImage

I'm trying to place an html5 image inside canvas. The image is translated, rotated and scaled using the css transform property. The main problem is how to copy the properties of the image and apply them on the canvas.
You can try the jsfiddle link below: the image can be scaled, moved and rotated and whatever appears in the greenbox should be the same as the one in the red.
https://jsfiddle.net/q7y1py5f/3/. Here I can move and scale the image, but when the rotation kicks in nothing works. Here is the code that bugs me (58 line in the javascript jsfiddle) :
context.save();
context.translate(image.width / 2, image.height / 2);
context.rotate(angle * Math.PI/180);
context.translate(((-image.width) / 2), ((-image.height) / 2));
context.translate(-x, -y);
context.scale(scale, scale);
context.drawImage(image, 0, 0);
context.restore();
Here the x and y are the difference between the top left of the green container and the top left of the image. Any help would be appreciated.
I've managed to get each property to work individually, but when combining them all together it doesn't work. When I only translate and scale the image and apply to the canvas context everything is fine, but when I add the rotation with the image is not where it's supposed to be.
I tried the following - calculated the top-left of the element with the angle to get the right value and then translating and rotating with it.
In function canvasCropping you write
var left = cropper.offset().left - img.offset().left,
top = cropper.offset().top - img.offset().top,
but rotating and scaling will change element offset, so you got wrong coordinats for translation. Change it to:
var left = -(cropper.offset().left - transform.translate.x-initialOffset.left),
top = -(cropper.offset().top -transform.translate.y-initialOffset.top),
initialOffset - initial offset of your image.
And some changes in drawRotatedImage
`
var drawRotatedImage = function (context, image, x, y, angle, width, height, scale) {
context.save();
// Move registration point to the center of the canvas
context.translate(x+image.width/2,y+image.height/2);
context.rotate(angle* Math.PI / 180);// angle must be in radians
context.scale(scale, scale);
// Move registration point back to the top left corner of canvas
context.translate((-image.width)/2, (-image.height)/2);
context.drawImage(image, 0, 0);
context.restore();
};
`

Canvas roulette wheel - using background images as each slice

I have a roulette wheel created with HTML5 canvas and currently each slice is a plain color generated by using the fillStyle(), beginPath(), stroke(), then fill() methods.
I would like to use images that that crop appropriately in the shape of the slice, but I'm not sure how to implement this using drawImage().
Here's the jsfiddle
http://jsfiddle.net/pyD2q/2/
Any help or resources is appreciated.
Instead of filling the color in you can use it as a clipping mask instead. For each arc, set clip, draw the image and repeat.
Something like this (untested):
for (i = 0; i < s.members.length; i++) {
angle = s.startAngle + i * s.arc;
ctx.beginPath();
ctx.arc(s.width / 2, s.height / 2, s.outsideRadius, angle, angle + s.arc, false);
ctx.arc(s.width / 2, s.height / 2, s.insideRadius, angle + s.arc, angle, true);
ctx.save(); /// store current clip-state
ctx.clip(); /// set current arc as clipping mask
ctx.drawImage(someImageArray[i], x, y, width, height);
ctx.restore(); /// restore clip-state
ctx.stroke(); /// stroke arc
}
You will of course also need to place the images correctly relative to the section and if you want to rotate the image you would need to rotate it at this point.
In that regard I would recommend creating this wheel on an off-screen canvas. This way you only need to draw the off-screen canvas into the main canvas rotated and what have you (saves you the trouble of calculating each angle as well and the performance will be better).

How to know the size of a scaled HTML 5 canvas context?

I'm writing an image to a canvas and then scaling the context and re-writing it to the same canvas. I can't figure out how to get the exact size of the scaled area once it's written back to the canvas. I need the size in pixels so I can calculate the translate offset to keep it centered on the canvas no matter the scale.
The canvas/context looks like this.
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
And I'm applying the scale like this (to scale it up by .1).
scale = scale * '1.1';
img_w = img.width * scale;
img_y = img.height * scale;
ctx.scale('1.1', '1.1');
Then, I'm using
ctx.drawImage(img, (canvas.width - img_w) / 2, (canvas.height - img_h) / 2);
to redraw the image and this is where it fails since I don't have the exact width of the scaled area.
Here's a fiddle http://jsfiddle.net/robertsdavidb/bGyFT/13/
The scaling works fine, but how do I keep it in the center after scaling?

Rotate Moving Object on Canvas

I'm trying to draw an object based on it's rotate property, but it seems to get confused when the object is moving.
ctx.save();
ctx.translate(this.width * 0.5, this.height*0.5);
ctx.rotate(DegToRad(45));
ctx.translate(-this.width*0.5,-this.height*0.5);
ctx.drawImage(this.img, this.spriteOffset, 0, this.width, this.height, this.x, this.y,this.width,this.height);
ctx.restore();
The image is drawn rotated 45 degrees, however it now moves in a down-left direction, when the object should only be moving downwards. The movement code is simply handled by incrementing the this.y position. Is there a simpler way to accomplish this?
This is because the whole canvas is being rotated.
What you could try is looking into a framework like Kinetic JS, which creates a sort of SVG API for the canvas.
This site has tons of information on how to use it as well, including rotation, transition, transformation, and pretty much anything else that you might need in working with it.
This should suit your needs rather well.
I think I should provide a low-level, framework-free option as well. Basically, using raw javascript and HTML to pull this off.
Now, as I understood your question, you are trying to make an object (let's assume a black square) move downwards AND have it spin. The only way I can think of to spin it in the canvas without going into path hell is to rotate the entire rendering context. BUT you can also import an image into canvas, for instance, a transparent black diamond (i.e. that same square rotated). So you'd use a separate canvas to render each step of the rotation for the square.
Essentially something like this:
var canvas2 = document.createElement('canvas'), ctx2 = canvas2.getContext('2d');
//do something with the second canvas
//let's assume the second canvas is the same size as the square
ctx.drawImage(canvas2, squareX, squareY);
See my attempt
As you can see, it is a bit wonky, but it does do essentially what the question asks; it moves the square down, and rotates it. It also plots the result of that rotation below the actual canvas so you can see what's happening under the hood, but the square cuts out due to the "center" being on the top left of the square, and not in the middle.
In the end, it really comes down to how you want to do it.
I was playing around with the API and found it was easier to just keep track of where the object should be. here's an example of a square moving diagonally across the screen and rotating.
<canvas id="canvas" style="width: 100%; height: 100%;">
</canvas>
<script>
var DELAY = 15; // ms
var RECT_WIDTH = 100; // px
var RECT_HEIGHT = 100; // px
var canvas = document.getElementById('canvas');
// set intrinsic dimensions
canvas.width = 1000;
canvas.height = 1000;
var ctx = canvas.getContext('2d')
ctx.fillStyle = 'teal';
var step = 0
var vx = 2
var animate = setInterval(function () {
ctx.resetTransform()
ctx.clearRect(0, 0, 1000, 1000);
ctx.translate(vx * step, vx * step);
// rotation in place, translate to center of square and back
ctx.translate(RECT_WIDTH / 2, RECT_HEIGHT / 2);
ctx.rotate((Math.PI / 180) * step);
ctx.translate(-(RECT_WIDTH / 2), -(RECT_HEIGHT / 2));
// Draw the rectangle
ctx.fillRect(0, 0, RECT_WIDTH, RECT_HEIGHT);
step = step + 1
}, DELAY)
setTimeout(function () {
clearInterval(animate);
}, 5000);
</script>
Here, I use vx and keep track of the steps to translate, and calculate what the rotation will be in radians based on the steps a new not caring what the previous state was. Make sure you rotate across the center of where you're square will be as well.
Use ctx.translate to set the object's position before applying the rotation. This should fix the problem.
ctx.save();
ctx.translate(this.x, this.y);
ctx.translate(this.width * 0.5, this.height*0.5);
ctx.rotate(DegToRad(45));
ctx.translate(-this.width*0.5,-this.height*0.5);
ctx.drawImage(this.img, this.spriteOffset, 0, this.width, this.height, 0, 0,this.width,this.height);
ctx.restore();

Categories

Resources