HTML5 Canvas scaling an image animation - javascript

I've created a straight forward linear HTML5 animation but I now want the image to continuously scale bigger then smaller until the end of the canvas' height.
What's the best way to do this?
Here's a JSFIDDLE
window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.oRequestAnimationFrame;
var canvas = document.getElementById('monopoly-piece');
var context = canvas.getContext('2d');
var img = document.createElement("img");
img.src = "images/tophat.png";
img.shadowBlur = 15;
img.shadowColor = "rgb(0, 0, 0)";
var xSpeed = 0; //px per ms
var ySpeed = 0.15;
function animate(nowTime, lastTime, xPos, yPos) {
// update
if ((img.style.height + yPos) > canvas.height) {
xPos = 0;
yPos = 0;
}
var timeDelta = nowTime - lastTime;
var xDelta = xSpeed * timeDelta;
var yDelta = ySpeed * timeDelta;
// clear
context.clearRect(0, 0, canvas.width, canvas.height);
// shadow
context.shadowOffsetX = 3;
context.shadowOffsetY = 7;
context.shadowBlur = 4;
context.shadowColor = 'rgba(0, 0, 0, 0.4)';
//draw img
context.drawImage(img, xPos, yPos);
if (yPos > canvas.height - img.height ) {
addMarker();
}
else {
requestAnimationFrame(
function(timestamp){
animate(timestamp, nowTime, xPos + xDelta, yPos + yDelta);
}
);
}
}
animate(0, 0, -10, 0);
Here is my current code which animates an image from the top of the canvas element to the bottom and then stops.
Thanks.

context.DrawImage has additional parameters that let you scale your image to your needs:
// var scale is the scaling factor to apply between 0.00 and 1.00
// scale=0.50 will draw the image half-sized
var scale=0.50;
// var y is the top position on the canvas where you want to drawImage your image
var y=0;
context.drawImage(
// draw img
img,
// clip a rectangular portion from img
// starting at [top,left]=[0,0]
// and with width,height == img.width,img.height
0, 0, img.width, img.height,
// draw that clipped portion of of img on the canvas
// at [top,left]=[0,y]
// with width scaled to img.width*scale
// and height scaled to img.height*scale
0, y, img.width*scale, img.height*scale
)
Here's an example you can start with and fit to your own design needs:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var cw = canvas.width;
var ch = canvas.height;
var scale = 1;
var scaleDirection = 1
var iw, ih;
var y = 1;
var yIncrement = ch / 200;
var img = new Image();
img.onload = start;
img.src = "https://dl.dropboxusercontent.com/u/139992952/multple/sun.png";
function start() {
iw = img.width;
ih = img.height;
requestAnimationFrame(animate);
}
function animate(time) {
if (scale > 0) {
requestAnimationFrame(animate);
}
ctx.clearRect(0, 0, cw, ch);
ctx.drawImage(img, 0, 0, iw, ih, 0, y, iw * scale / 100, ih * scale / 100);
scale += scaleDirection;
if (scale > 100) {
scale = 100;
scaleDirection *= -1;
}
y += yIncrement;
}
$("#test").click(function() {
scale = y = scaleDirection = 1;
requestAnimationFrame(animate);
});
body{ background-color: ivory; }
canvas{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<button id="test">Animate Again</button>
<br>
<canvas id="canvas" width=80 height=250></canvas>

Related

How can I rotate the canvas image?

I tried to load the image by url on the canvas. And I change the canvas size to image size. andf I tried to rotate the image but if I executed it, some part of the image is cropped. Please find the errors in my code.
if (submitBtn) {
submitBtn.addEventListener('click', handleSubmitBtnClick);
}
function handleSubmitBtnClick() {
let imgURLValue = inputImageURL.value;
backgroundImage.src = `${imgURLValue}`;
backgroundImage.crossOrigin = 'Anonymous';
backgroundImage.onload = function () {
canvas.width = backgroundImage.width;
canvas.height = backgroundImage.height;
ctx.drawImage(
backgroundImage,
0,
0,
backgroundImage.width,
backgroundImage.height
);
};
}
if (rotateBtn) {
rotateBtn.addEventListener('click', handleRotateBtn);
}
function handleRotateBtn() {
degree += 90;
if (degree >= 360) {
degree = 0;
}
if (degree === 0 || degree === 180) {
canvas.width = backgroundImage.width;
canvas.height = backgroundImage.height;
} else {
canvas.width = backgroundImage.height;
canvas.height = backgroundImage.width;
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.save();
ctx.translate(backgroundImage.width / 2, backgroundImage.height / 2);
ctx.rotate((degree * Math.PI) / 180);
ctx.drawImage(
backgroundImage,
0,
0,
backgroundImage.width,
backgroundImage.height,
-backgroundImage.width / 2,
-backgroundImage.height / 2,
backgroundImage.width,
backgroundImage.height
);
ctx.restore();
}
[enter image description here][1]
If I rotate the image, the image is loaded like this..
[1]: https://i.stack.imgur.com/drNaG.png
The origin of your rotation should be the center of the canvas, so that it uses the updated width and height values.
By using the original center of the image your drawing will be offset.
const rotateBtn = document.querySelector("button");
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
const backgroundImage = new Image();
backgroundImage.src = "https://picsum.photos/150/300";
backgroundImage.decode().then(() => {
rotateBtn.disabled = false;
handleRotateBtn();
});
let degree = -90;
if (rotateBtn) {
rotateBtn.addEventListener('click', handleRotateBtn);
}
function handleRotateBtn() {
degree += 90;
if (degree >= 360) {
degree = 0;
}
if (degree === 0 || degree === 180) {
canvas.width = backgroundImage.width;
canvas.height = backgroundImage.height;
} else {
canvas.width = backgroundImage.height;
canvas.height = backgroundImage.width;
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.save();
// use the updated center as origin
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate((degree * Math.PI) / 180);
// I simplified this call but it does the same as yours
ctx.drawImage(
backgroundImage,
-backgroundImage.width / 2,
-backgroundImage.height / 2
);
ctx.restore();
}
canvas { background: ivory }
<button disabled>rotate</button><br>
<canvas></canvas>

Adjust canvas image size like 'background-size: cover' and responsive

What I want to do
Set background-size:cover-like effect on a canvas image
Re-draw a canvas image as a window is resized (responsive)
What I tried
I tried 2 ways below. Neither works.
Simulation background-size: cover in canvas
How to set background size cover on a canvas
Issue
The image's aspect ratio is not fixed.
I am not sure but the image does not seem to be re-rendered as a window is resized.
My code
function draw() {
// Get <canvas>
const canvas = document.querySelector('#canvas');
// Canvas
const ctx = canvas.getContext('2d');
const cw = canvas.width;
const ch = canvas.height;
// Image
const img = new Image();
img.src = 'https://source.unsplash.com/WLUHO9A_xik/1600x900';
img.onload = function() {
const iw = img.width;
const ih = img.height;
// 'background-size:cover'-like effect
const aspectHeight = ih * (cw / iw);
const heightOffset = ((aspectHeight - ch) / 2) * -1;
ctx.drawImage(img, 0, heightOffset, cw, aspectHeight);
};
}
window.addEventListener('load', draw);
window.addEventListener('resize', draw);
canvas {
display: block;
/* canvas width depends on parent/window width */
width: 90%;
height: 300px;
margin: 0 auto;
border: 1px solid #ddd;
}
<canvas id="canvas"></canvas>
The canvas width / height attributes do not reflect it's actual size in relation to it being sized with CSS. Given your code, cw/ch are fixed at 300/150. So the whole calculation is based on incorrect values.
You need to use values that actually reflect it's visible size. Like clientWidth / clientHeight.
A very simple solution is to update the canvas width/height attributes before using them for any calculations. E.g.:
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
Full example:
const canvas = document.querySelector('#canvas');
const ctx = canvas.getContext('2d');
// only load the image once
const img = new Promise(r => {
const img = new Image();
img.src = 'https://source.unsplash.com/WLUHO9A_xik/1600x900';
img.onload = () => r(img);
});
const draw = async () => {
// resize the canvas to match it's visible size
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
const loaded = await img;
const iw = loaded.width;
const ih = loaded.height;
const cw = canvas.width;
const ch = canvas.height;
const f = Math.max(cw/iw, ch/ih);
ctx.setTransform(
/* scale x */ f,
/* skew x */ 0,
/* skew y */ 0,
/* scale y */ f,
/* translate x */ (cw - f * iw) / 2,
/* translate y */ (ch - f * ih) / 2,
);
ctx.drawImage(loaded, 0, 0);
};
window.addEventListener('load', draw);
window.addEventListener('resize', draw);
.--dimensions {
width: 90%;
height: 300px;
}
.--border {
border: 3px solid #333;
}
canvas {
display: block;
margin: 0 auto;
}
#test {
background: url(https://source.unsplash.com/WLUHO9A_xik/1600x900) no-repeat center center;
background-size: cover;
margin: 1rem auto 0;
}
<canvas id="canvas" class="--dimensions --border"></canvas>
<div id="test" class="--dimensions --border"></div>
First, load only once your image, currently you are reloading it every time the page is resized.
Then, your variables cw and ch will always be 300 and 150 since you don't set the size of your canvas. Remember, the canvas has two different sizes, its layout one (controlled by CSS) and its buffer one (controlled by its .width and .height attributes).
You can retrieve the layout values through the element's .offsetWidth and .offsetHeight properties.
Finally, your code does a contain image-sizing. To do a cover, you can refer to the answers you linked to, and particularly to K3N's one
{
// Get <canvas>
const canvas = document.querySelector('#canvas');
// Canvas
const ctx = canvas.getContext('2d');
// Image
const img = new Image();
img.src = 'https://source.unsplash.com/WLUHO9A_xik/1600x900';
function draw() {
// get the correct dimension as calculated by CSS
// and set the canvas' buffer to this dimension
const cw = canvas.width = canvas.offsetWidth;
const ch = canvas.height = canvas.offsetHeight;
if( !inp.checked ) {
drawImageProp(ctx, img, 0, 0, cw, ch, 0, 0);
}
}
img.onload = () => {
window.addEventListener('resize', draw);
draw();
};
inp.oninput = draw;
}
// by Ken Fyrstenberg https://stackoverflow.com/a/21961894/3702797
function drawImageProp(ctx, img, x, y, w, h, offsetX, offsetY) {
if (arguments.length === 2) {
x = y = 0;
w = ctx.canvas.width;
h = ctx.canvas.height;
}
// default offset is center
offsetX = typeof offsetX === "number" ? offsetX : 0.5;
offsetY = typeof offsetY === "number" ? offsetY : 0.5;
// keep bounds [0.0, 1.0]
if (offsetX < 0) offsetX = 0;
if (offsetY < 0) offsetY = 0;
if (offsetX > 1) offsetX = 1;
if (offsetY > 1) offsetY = 1;
var iw = img.width,
ih = img.height,
r = Math.min(w / iw, h / ih),
nw = iw * r, // new prop. width
nh = ih * r, // new prop. height
cx, cy, cw, ch, ar = 1;
// decide which gap to fill
if (nw < w) ar = w / nw;
if (Math.abs(ar - 1) < 1e-14 && nh < h) ar = h / nh; // updated
nw *= ar;
nh *= ar;
// calc source rectangle
cw = iw / (nw / w);
ch = ih / (nh / h);
cx = (iw - cw) * offsetX;
cy = (ih - ch) * offsetY;
// make sure source rectangle is valid
if (cx < 0) cx = 0;
if (cy < 0) cy = 0;
if (cw > iw) cw = iw;
if (ch > ih) ch = ih;
// fill image in dest. rectangle
ctx.drawImage(img, cx, cy, cw, ch, x, y, w, h);
}
canvas {
display: block;
/* canvas width depends on parent/window width */
width: 90%;
height: 300px;
margin: 0 auto;
border: 1px solid #ddd;
background-image: url('https://source.unsplash.com/WLUHO9A_xik/1600x900');
background-size: cover;
background-repeat: no-repeat;
}
<label><input id="inp" type="checkbox">show CSS rendering</label>
<canvas id="canvas"></canvas>

How to scale an image for a canvas pattern?

I want to fill a Canvas with an Image and scale it to a certain width beforehand.
I am trying to achieve an effect where an image in the foreground of the canvas can be erased with the mouse to view an image in the background. This is why I need to use a pattern to fill my canvas instead of just using drawImage(). Everything works apart from the scaling of the foreground image. Here is my code for generating the pattern:
var blueprint_background = new Image();
blueprint_background.src = "myfunurl";
blueprint_background.width = window.innerWidth;
blueprint_background.onload = function(){
var pattern = context.createPattern(this, "no-repeat");
context.fillStyle = pattern;
context.fillRect(0, 0, window.innerWidth, 768);
context.fill();
};
This does exactly what it should do, except that the image keeps its original size.
As you see, I want the image to scale to window.innerWidth (which has the value 1920 when logging it).
If needed, I can provide the rest of the code, but since the error is most likely in this snippet, I decided not to post the rest.
EDIT: Here is my full code with the suggested changes. The front ground image now displays over the full width, however the erasing does not work anymore.
JavaScript (Note that I use jQuery instead of $):
jQuery(document).ready(function() {
var cwidth = window.innerWidth;
var cheight = 768;
function createCanvas(parent, width, height) {
var canvas = {};
canvas.node = document.createElement('canvas');
canvas.context = canvas.node.getContext('2d');
canvas.node.width = width || 100;
canvas.node.height = height || 100;
parent.appendChild(canvas.node);
return canvas;
}
function init(canvas, fillColor) {
var ctx = canvas.context;
canvas.isDrawing = true;
jQuery('#canvas').children().css('position:absolute; top: ' + jQuery('#Top_bar').height() + 'px');
// define a custom fillCircle method
ctx.fillCircle = function(x, y, radius, fillColor) {
this.fillStyle = fillColor;
this.beginPath();
this.moveTo(x, y);
this.arc(x, y, radius, 0, Math.PI * 2, false);
this.fill();
};
// bind mouse events
canvas.onmousemove = function(e) {
if (!canvas.isDrawing) {
return;
}
var x = e.pageX - this.offsetLeft;
var y = e.pageY - jQuery('#Top_bar').outerHeight();
var radius = 30;
var fillColor = '#ff0000';
ctx.globalCompositeOperation = 'destination-out';
ctx.fillCircle(x, y, radius, fillColor);
};
}
var container = document.getElementById('canvas');
jQuery('#canvas').css('position:absolute; top: ' + jQuery('#Top_bar').height() + 'px');
var canvas = createCanvas(container, cwidth, cheight);
init(canvas, '#ddd');
var fgimg = document.getElementById("fgimg");
fgimg.width = cwidth;
var context = canvas.node.getContext("2d");
let canvasP = document.getElementById("pattern");
canvasP.width = window.innerWidth;
canvasP.height = 768;
let ctxP = canvasP.getContext("2d");
ctxP.drawImage( fgimg, 0, 0,window.innerWidth,768 );
context.fillStyle = context.createPattern(canvasP,"no-repeat");
context.fillRect(0,0, canvas.width, canvas.height);
});
CSS:
#canvas {
background:url(http://ulmke-web.de/wp-content/uploads/2019/01/Header-6.jpg);
background-repeat: no-repeat;
background-size: cover;
background-position: center center;
width: 100%;
height: 768px;
}
HTML:
<div id="canvas">
<canvas id="pattern">
</div>
<div style="display:none">
<img id="fgimg" src=" http://ulmke-web.de/wp-content/uploads/2019/01/Header-5.jpg">
</div>
I would use two canvases. On the first one you draw your image and you use this canvas as an image to create the pattern. In order to scale the image you scale the size of the first canvas #pattern in my example.
For example you can do this for a 10/10 image:
canvasP.width = 10;
canvasP.height = 10;
ctxP.drawImage( redpoint, 2.5, 2.5 );
or you can do this for a 20/20 image:
canvasP.width = 20;
canvasP.height = 20;
ctxP.drawImage( redpoint, 5, 5,10,10 );
Furthermore, in my example I'm adding a little margin around the image.
let canvasP = document.getElementById("pattern");
if (canvasP && canvasP.getContext) {
let ctxP = canvasP.getContext("2d");
/*canvasP.width = 10;
canvasP.height = 10;
ctxP.drawImage( redpoint, 2.5, 2.5 ); */
canvasP.width = 20;
canvasP.height = 20;
ctxP.drawImage( redpoint, 5, 5,10,10 );
}
let canvas1 = document.getElementById("canvas");
if (canvas1 && canvas1.getContext) {
let ctx1 = canvas1.getContext("2d");
if (ctx1) {
ctx1.fillStyle = ctx1.createPattern(canvasP,"repeat");
ctx1.fillRect(0,0, canvas1.width, canvas1.height);
}
}
canvas{border:1px solid}
<img id="redpoint" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO 9TXL0Y4OHwAAAABJRU5ErkJggg==">
<canvas id="pattern"></canvas>
<canvas id="canvas"></canvas>
I hope it helps.

Resize image in html5 canvas

I'm taking image input from user, then resizing and showing it on a canvas. Here is the code-
HTML-
<form class="cmxform">
<input type='file' id="Photo" />
<canvas id="canvas" width="300" height="200"></canvas>
</form>
JavaScript-
$("#Photo").change(function (e) {
var ctx = document.getElementById('canvas').getContext('2d');
var img = new Image();
img.src = URL.createObjectURL(e.target.files[0]);
img.onload = function () {
ctx.drawImage(img, 0, 0, img.width, img.height, // source rectangle
0, 0, canvas.width, canvas.height); // destination rectangle
}
});
But here I'm loosing the aspect ratio. Is there any way to do it?
UPDATE-
I've got the answer from this sa question -
Here is a snippet to help you for that
var ctx = document.getElementById('canvas').getContext('2d');
var img = new Image();
var fill = true;
if (fill)
{
$('#fill').attr("disabled", true);
}
$("#Photo").change(function (e) {
img.src = URL.createObjectURL(e.target.files[0]);
img.onload = function () {
if (fill)
{
drowImageFill(img);
}
else
{
drowImageCenter(img);
}
}
});
$("#fill").click(function(){
//console.log("fill");
var input = document.getElementById('Photo');
if (input.files[0] !== undefined)
{
img.src = URL.createObjectURL(input.files[0]);
img.onload = function () {
drowImageFill(img);
}
}
$('#fill').attr("disabled", true);
$('#center').attr("disabled", false);
fill = true;
});
$("#center").click(function(){
//console.log("center");
var input = document.getElementById('Photo');
if (input.files[0] !== undefined)
{
img.src = URL.createObjectURL(input.files[0]);
img.onload = function () {
drowImageCenter(img);
}
}
$('#center').attr("disabled", true);
$('#fill').attr("disabled", false);
fill = false;
});
//ratio formula
//http://andrew.hedges.name/experiments/aspect_ratio/
function drowImageFill(img){
ctx.clearRect(0, 0, canvas.width, canvas.height);
//detect closet value to canvas edges
if( img.height / img.width * canvas.width > canvas.height)
{
// fill based on less image section loss if width matched
var width = canvas.width;
var height = img.height / img.width * width;
offset = (height - canvas.height) / 2;
ctx.drawImage(img, 0, -offset, width, height);
}
else
{
// fill based on less image section loss if height matched
var height = canvas.height;
var width = img.width / img.height * height;
offset = (width - canvas.width) / 2;
ctx.drawImage(img, -offset , 0, width, height);
}
}
function drowImageCenter(img)
{
ctx.clearRect(0, 0, canvas.width, canvas.height);
if( img.height / img.width * canvas.width < canvas.height)
{
// center based on width
var width = canvas.width;
var height = img.height / img.width * width;
offset = (canvas.height - height) / 2;
ctx.drawImage(img, 0, offset, width, height);
}
else
{
// center based on height
var height = canvas.height;
var width = img.width / img.height * height;
offset = (canvas.width - width) / 2;
ctx.drawImage(img, offset , 0, width, height);
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="canvas" width="300" height="200" style="border:2px solid #000000;"></canvas>
<form class="cmxform">
<input type='file' id="Photo" />
</form>
<button id="fill">Fill</button>
<button id="center">Center</button>

Rotate individual objects in canvas?

the rotate() function seems to rotate the whole drawing area. Is there a way to rotate paths individually? I want the center for the rotation to be the object, not the drawing area.
Using save() and restore() still makes rotate take into account the whole drawing area.
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
context.save();
context.fillStyle = 'red';
context.rotate(0.35);
context.fillRect(40,40, 100, 100);
context.restore();
context.save();
context.fillStyle = 'blue';
context.rotate(0.35);
context.fillRect(200, 40, 100, 100);
context.restore();
<canvas id="canvas" width="500" height="500"></canvas>
Use local space
Instead of drawing object at the position you want them draw everything around its own origin in its local space. The origin is at (0,0) and is the location that the object rotates around.
So if you have a rectangle that you draw with
function drawRect(){
context.fillRect(200, 40, 100, 100);
}
change it so that it is drawn at its origin
function drawRect(){
context.fillRect(-50,-50 , 100, 100);
}
Now you can easily draw it wherevery you want
Start with the setTransform function as that clears any existing tranforms and is a convenient way to set the location of the center of the object will be
ctx.setTransform(1,0,0,1,posX,posY); // clear transform and set center location
if you want to rotate it then add the rotation
ctx.rotate(ang);
and scale with
ctx.scale(scale,scale);
if you have two different scales you should scale before the rotate.
Now just call the draw function
drawRect();
and it is drawn with its center at posX,posY rotated and scaled.
You can combine it all into a function that has the x,y position, the width and the height, scale and rotation. You can include the scale in the setTransform
function drawRect(x,y,w,h,scale,rotation){
ctx.setTransform(scale,0,0,scale,x,y);
ctx.rotate(rotation);
ctx.strokeRect(-w/2,-h/2,w,h);
}
It also applies to an image as a sprite, and I will include a alpha
function drawImage(img,x,y,w,h,scale,rotation,alpha){
ctx.globalAlpha = alpha;
ctx.setTransform(scale,0,0,scale,x,y);
ctx.rotate(rotation);
ctx.drawImage(img,-img.width/2,-img.height/2,img.width,img.height);
}
On a 6 year old laptop that can draw 2000 sprites on firefox every 1/60th of a second, each rotated, scaled, positioned, and with a alpha fade.
No need to mess about with translating back and forward. Just keep all the objects you draw around there own origins and move that origin via the transform.
Update
Lost the demo so here it is to show how to do it in practice.
Just draws a lot of rotated, scaled translated, alphaed rectangles.
By using setTransform you save a lot of time by avoiding save and restore
// create canvas and add resize
var canvas,ctx;
function createCanvas(){
canvas = document.createElement("canvas");
canvas.style.position = "absolute";
canvas.style.left = "0px";
canvas.style.top = "0px";
canvas.style.zIndex = 1000;
document.body.appendChild(canvas);
}
function resizeCanvas(){
if(canvas === undefined){
createCanvas();
}
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
ctx = canvas.getContext("2d");
}
resizeCanvas();
window.addEventListener("resize",resizeCanvas);
// simple function to draw a rectangle
var drawRect = function(x,y,w,h,scale,rot,alpha,col){
ctx.setTransform(scale,0,0,scale,x,y);
ctx.rotate(rot);
ctx.globalAlpha = alpha;
ctx.strokeStyle = col;
ctx.strokeRect(-w/2,-h/2, w, h);
}
// create some rectangles in unit scale so that they can be scaled to fit
// what ever screen size this is in
var rects = [];
for(var i = 0; i < 200; i ++){
rects[i] = {
x : Math.random(),
y : Math.random(),
w : Math.random() * 0.1,
h : Math.random() * 0.1,
scale : 1,
rotate : 0,
dr : (Math.random() - 0.5)*0.1, // rotation rate
ds : Math.random()*0.01, // scale vary rate
da : Math.random()*0.01, // alpha vary rate
col : "hsl("+Math.floor(Math.random()*360)+",100%,50%)",
};
}
// draw everything once a frame
function update(time){
var w,h;
w = canvas.width; // get canvas size incase there has been a resize
h = canvas.height;
ctx.setTransform(1,0,0,1,0,0); // reset transform
ctx.clearRect(0,0,w,h); // clear the canvas
// update and draw each rect
for(var i = 0; i < rects.length; i ++){
var rec = rects[i];
rec.rotate += rec.dr;
drawRect(rec.x * w, rec.y * h, rec.w * w,rec.h * h,rec.scale + Math.sin(time * rec.ds) * 0.4,rec.rotate,Math.sin(time * rec.da) *0.5 + 0.5,rec.col);
}
requestAnimationFrame(update); // do it all again
}
requestAnimationFrame(update);
All transformations in canvas are for the whole drawing area. If you want to rotate around a point you're going to have to translate that point to the origin, do your rotation and translate it back. Something like this is what you want.
Use a rotate function to rotate all of the shape's points around its center.
<!DOCTYPE html>
<html>
<head>
<style>
body
{
margin: 0px;
padding: 0px;
overflow: hidden;
}
canvas
{
position: absolute;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
var canvas;
var context;
canvas = document.getElementById("canvas");
context = canvas.getContext("2d");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var degreesToRadians = function(degrees)
{
return degrees*Math.PI/180;
}
var rotate = function(x, y, cx, cy, degrees)
{
var radians = degreesToRadians(degrees);
var cos = Math.cos(radians);
var sin = Math.sin(radians);
var nx = (cos * (x - cx)) + (sin * (y - cy)) + cx;
var ny = (cos * (y - cy)) - (sin * (x - cx)) + cy;
return new Vector2(nx, ny);
}
var Vector2 = function(x, y)
{
return {x:x,y:y};
}
var Shape = function(points, color)
{
this.color = color;
this.points = points;
};
Shape.prototype.rotate = function(degrees)
{
var center = this.getCenter();
for (var i = 0; i < this.points.length; i++)
{
this.points[i] = rotate(this.points[i].x,this.points[i].y,center.x,center.y,degrees);
}
context.beginPath();
context.arc(center.x,center.y,35,0,Math.PI*2);
context.closePath();
context.stroke();
}
Shape.prototype.draw = function()
{
context.fillStyle = this.color;
context.strokeStyle = "#000000";
context.beginPath();
context.moveTo(this.points[0].x, this.points[0].y);
for (var i = 0; i < this.points.length; i++)
{
context.lineTo(this.points[i].x, this.points[i].y);
//context.fillText(i+1, this.points[i].x, this.points[i].y);
}
context.closePath();
context.fill();
context.stroke();
}
Shape.prototype.getCenter = function()
{
var center = {x:0,y:0};
for (var i = 0; i < this.points.length; i++)
{
center.x += this.points[i].x;
center.y += this.points[i].y;
}
center.x /= this.points.length;
center.y /= this.points.length;
return center;
}
Shape.prototype.translate = function(x, y)
{
for (var i = 0; i < this.points.length; i++)
{
this.points[i].x += x;
this.points[i].y += y;
}
}
var Rect = function(x,y,w,h,c)
{
this.color = c;
this.points = [Vector2(x,y),Vector2(x+w,y),Vector2(x+w,y+h),Vector2(x,y+h)];
}
Rect.prototype = Shape.prototype;
var r = new Rect(50, 50, 200, 100, "#ff0000");
r.draw();
r.translate(300,0);
r.rotate(30);
r.draw();
</script>
</body>
</html>

Categories