Update HTML5 canvas rectangle on hover? - javascript

I've got some code which draws a rectangle on a canvas, but I want that rectangle to change color when I hover the mouse over it.
The problem is after I've drawn the rectangle I'm not sure how I select it again to make the adjustment.
What I want to do:
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.rect(20,20,150,100);
ctx.stroke();
$('c.[rectangle]').hover(function(this){
this.fillStyle = 'red';
this.fill();
});

You can't do this out-of-the-box with canvas. Canvas is just a bitmap, so the hover logic has to be implemented manually.
Here is how:
Store all the rectangles you want as simple object
For each mouse move on the canvas element:
Get mouse position
Iterate through the list of objects
use isPointInPath() to detect a "hover"
Redraw both states
Example
var canvas = document.querySelector("canvas"),
ctx = canvas.getContext("2d"),
rects = [
{x: 10, y: 10, w: 200, h: 50},
{x: 50, y: 70, w: 150, h: 30} // etc.
], i = 0, r;
// render initial rects.
while(r = rects[i++]) ctx.rect(r.x, r.y, r.w, r.h);
ctx.fillStyle = "blue"; ctx.fill();
canvas.onmousemove = function(e) {
// important: correct mouse position:
var rect = this.getBoundingClientRect(),
x = e.clientX - rect.left,
y = e.clientY - rect.top,
i = 0, r;
ctx.clearRect(0, 0, canvas.width, canvas.height); // for demo
while(r = rects[i++]) {
// add a single rect to path:
ctx.beginPath();
ctx.rect(r.x, r.y, r.w, r.h);
// check if we hover it, fill red, if not fill it blue
ctx.fillStyle = ctx.isPointInPath(x, y) ? "red" : "blue";
ctx.fill();
}
};
<canvas/>

This is a stable code in base of #K3N answer. The basic problem of his code is because when one box is over the another the two may get mouse hover at same time. My answer perfectly solves that adding a 'DESC' to 'ASC' loop.
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d");
var map = [
{x: 20, y: 20, w: 60, h: 60},
{x: 30, y: 50, w: 76, h: 60}
];
var hover = false, id;
var _i, _b;
function renderMap() {
for(_i = 0; _b = map[_i]; _i ++) {
ctx.fillStyle = (hover && id === _i) ? "red" : "blue";
ctx.fillRect(_b.x, _b.y, _b.w, _b.h);
}
}
// Render everything
renderMap();
canvas.onmousemove = function(e) {
// Get the current mouse position
var r = canvas.getBoundingClientRect(),
x = e.clientX - r.left, y = e.clientY - r.top;
hover = false;
ctx.clearRect(0, 0, canvas.width, canvas.height);
for(var i = map.length - 1, b; b = map[i]; i--) {
if(x >= b.x && x <= b.x + b.w &&
y >= b.y && y <= b.y + b.h) {
// The mouse honestly hits the rect
hover = true;
id = i;
break;
}
}
// Draw the rectangles by Z (ASC)
renderMap();
}
<canvas id="canvas"></canvas>

You may have to track the mouse on the canvas using JavaScript and see when it is over your rectangle and change the color then. See code below from my blog post
<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="700" height="500" style="border:1px solid #c3c3c3;">
Your browser does not support the HTML5 canvas tag.
</canvas>
<script>
var myRect={x:150, y:75, w:50, h:50, color:"red"};
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.fillStyle = myRect.color;
ctx.fillRect(myRect.x, myRect.y, myRect.w, myRect.h);
c.addEventListener("mousemove", function(e){
if ((e.clientX>=myRect.x)&(e.clientX<=myRect.x+myRect.w)&(e.clientY>=myRect.y)&(e.clientY<=myRect.y+myRect.h)){
myRect.color = "green";}
else{
myRect.color = "red";}
updateCanvas();
}, false);
function updateCanvas(){
ctx.fillStyle = myRect.color;
ctx.fillRect(myRect.x, myRect.y, myRect.w, myRect.h);
}
</script>
</body>
</html>

I believe this is a slightly more in-depth answer that would work better for you, especially if you are interested in game design with the canvas element.
The main reason this would work better for you is because it focuses more on an OOP (object orientated programming) approach. This allows for objects to be defined, tracked and altered at a later time via some event or circumstance. It also allows for easy scaling of your code and in my opinion is just more readable and organized.
Essentially what you have here is two shapes colliding. The cursor and the individual point / object it hovers over. With basic squares, rectangles or circles this isn't too bad. But, if you are comparing two more unique shapes, you'll need to read up more on Separating Axis Theorem (SAT) and other collision techniques. At that point optimizing and performance will become a concern, but for now I think this is the optimal approach.
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const width = canvas.width = window.innerWidth;
const height = canvas.height = window.innerHeight;
const cx = width / 2;
const cy = height / 2;
const twoPie = Math.PI * 2;
const points = []; // This will be the array we store our hover points in later
class Point {
constructor(x, y, r) {
this.x = x;
this.y = y;
this.r = r || 0;
}
}
class HoverPoint extends Point {
constructor(x, y, r, color, hoverColor) {
super(x, y, r);
this.color = color;
this.hoverColor = hoverColor;
this.hovered = false;
this.path = new Path2D();
}
draw() {
this.hovered ? ctx.fillStyle = this.hoverColor : ctx.fillStyle = this.color;
this.path.arc(this.x, this.y, this.r, 0, twoPie);
ctx.fill(this.path);
}
}
class Cursor extends Point {
constructor(x, y, r) {
super(x, y, r);
}
collisionCheck(points) {
// This is the method that will be called during the animate function that
// will check the cursors position against each of our objects in the points array.
document.body.style.cursor = "default";
points.forEach(point => {
point.hovered = false;
if (ctx.isPointInPath(point.path, this.x, this.y)) {
document.body.style.cursor = "pointer";
point.hovered = true;
}
});
}
}
function createPoints() {
// Create your points and add them to the points array.
points.push(new HoverPoint(cx, cy, 100, 'red', 'coral'));
points.push(new HoverPoint(cx + 250, cy - 100, 50, 'teal', 'skyBlue'));
// ....
}
function update() {
ctx.clearRect(0, 0, width, height);
points.forEach(point => point.draw());
}
function animate(e) {
const cursor = new Cursor(e.offsetX, e.offsetY);
update();
cursor.collisionCheck(points);
}
createPoints();
update();
canvas.onmousemove = animate;
There is one more thing that I would like to suggest. I haven't done tests on this yet but I suspect that using some simple trigonometry to detect if our circular objects collide would preform better over the ctx.IsPointInPath() method.
However if you are using more complex paths and shapes, then the ctx.IsPointInPath() method would most likely be the way to go. if not some other more extensive form of collision detection as I mentioned earlier.
The resulting change would look like this...
class Cursor extends Point {
constructor(x, y, r) {
super(x, y, r);
}
collisionCheck(points) {
document.body.style.cursor = "default";
points.forEach(point => {
let dx = point.x - this.x;
let dy = point.y - this.y;
let distance = Math.hypot(dx, dy);
let dr = point.r + this.r;
point.hovered = false;
// If the distance between the two objects is less then their combined radius
// then they must be touching.
if (distance < dr) {
document.body.style.cursor = "pointer";
point.hovered = true;
}
});
}
}
here is a link containing examples an other links related to collision detection
I hope you can see how easily something like this can be modified and used in games and whatever else. Hope this helps.

Below code adds shadow to canvas circle on hovering it.
<html>
<body>
<canvas id="myCanvas" width="1000" height="500" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas>
</body>
<script>
var canvas = document.getElementById("myCanvas"),
ctx = canvas.getContext("2d"),
circle = [{
x: 60,
y: 50,
r: 40,
},
{
x: 100,
y: 150,
r: 50,
} // etc.
];
// render initial rects.
for (var i = 0; i < circle.length; i++) {
ctx.beginPath();
ctx.arc(circle[i].x, circle[i].y, circle[i].r, 0, 2 * Math.PI);
ctx.fillStyle = "blue";
ctx.fill();
}
canvas.onmousemove = function(e) {
var x = e.pageX,
y = e.pageY,
i = 0,
r;
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let i = 0; i < circle.length; i++) {
if ((x > circle[i].x - circle[i].r) && (y > circle[i].y - circle[i].r) && (x < circle[i].x + circle[i].r) && (y < circle[i].y + circle[i].r)) {
ctx.beginPath();
ctx.arc(circle[i].x, circle[i].y, circle[i].r, 0, 2 * Math.PI);
ctx.fillStyle = "blue";
ctx.fill();
ctx.shadowBlur = 10;
ctx.lineWidth = 3;
ctx.strokeStyle = 'rgb(255,255,255)';
ctx.shadowColor = 'grey';
ctx.stroke();
ctx.shadowColor = 'white';
ctx.shadowBlur = 0;
} else {
ctx.beginPath();
ctx.arc(circle[i].x, circle[i].y, circle[i].r, 0, 2 * Math.PI);
ctx.fillStyle = "blue";
ctx.fill();
ctx.shadowColor = 'white';
ctx.shadowBlur = 0;
}
}
};
</script>
</html>

I know this is old, but I am surprised no one has mentioned JCanvas. It adds to the simplicity of animating canvas on events. More documentation here https://projects.calebevans.me/jcanvas/docs/mouseEvents/
<html lang="en">
<head>
<!-- css and other -->
</head>
<body onload="draw();">
<canvas id = "canvas" width="500" height="500" style= border:1px solid #000000;"> </canvas>
<script>
function draw() {
$('canvas').drawRect({
layer: true,
fillStyle:'#333',
x:100, y: 200,
width: 600,
height: 400,
mouseover: function(layer) {
$(this).animateLayer(layer, {
fillStyle: 'green'
}, 1000, 'swing');
}
});
}
<script src="https://code.jquery.com/jquery-3.3.1.min.js" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jcanvas/21.0.1/jcanvas.js" crossorigin="anonymous"></script>
</body>
</html>

Consider this following code:
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.rect(20,20,150,100);
ctx.stroke();
c.addEventListener("mouseover", doMouseOver, false);//added event to canvas
function doMouseOver(e){
ctx.fillStyle = 'red';
ctx.fill();
}
DEMO

You could use canvas.addEventListener
var canvas = document.getElementById('canvas0');
canvas.addEventListener('mouseover', function() { /*your code*/ }, false);
It worked on google chrome

var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.rect(20,20,150,100);
ctx.stroke();
$(c).hover(function(e){
ctx.fillStyle = 'red';
ctx.fill();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<canvas id="myCanvas"/>

Related

DrawImage() doesn't draw on canvas

I am trying to make a screen following the player so that the player is in the middle of the screen. I have already made it in another game, but here it doesn't work. Here is my code :
var c = document.getElementById("main");
var ctx = c.getContext("2d");
var screen = document.getElementById("screen").getContext("2d");
var WhatUSeeWidth = document.getElementById("screen").width;
var WhatUSeeHeight = document.getElementById("screen").height;
ctx.beginPath();
for (i = 0; i < 100; i ++) {
if (i % 2) {
ctx.fillStyle = "red";
}
else {
ctx.fillStyle = "blue";
}
ctx.fillRect(0, i * 100, 500, 100);
}
var player = {
x : 700,
y : 800
}
setInterval(tick, 100);
function tick() {
screen.beginPath();
screen.drawImage(c, player.x - WhatUSeeWidth / 2, player.y - WhatUSeeHeight / 2, WhatUSeeWidth, WhatUSeeHeight, 0, 0, WhatUSeeWidth, WhatUSeeHeight);
}
canvas {
border: 2px solid black;
}
<canvas id="main" width="500" height="500"h></canvas>
<canvas id="screen" width="500" height="500"></canvas>
I want to draw The Blue and red canvas in The "screen" canvas Using drawImage
Ok , from your comment I understood what you are looking for. But the problem is that you probably start by an example without having understood. I try to give you my interpretation of what you do , but you should look for a good guide that starts with the basics and deepen animations (for example this: http://www.html5canvastutorials.com/).
HTML
<canvas id="canvasLayer" width="500" height="500"></canvas>
Javascript
var canvas = document.getElementById("canvasLayer");
var context = canvas.getContext("2d");
var WhatUSeeWidth = document.getElementById("canvasLayer").width;
var WhatUSeeHeight = document.getElementById("canvasLayer").height;
var player = {
x : 0,
y : 0
}
function drawBackground() {
for (i = 0; i < 100; i ++) {
if (i % 2) {
context.fillStyle = "red";
}
else {
context.fillStyle = "blue";
}
context.fillRect(0, i * 100, 500, 100);
}
}
function playerMove() {
context.beginPath();
var radius = 5;
context.arc(player.x, player.y, radius, 0, 2 * Math.PI, false);
context.fillStyle = 'green';
context.fill();
context.lineWidth = 1;
context.strokeStyle = '#003300';
context.stroke();
}
setInterval(tick, 100);
function tick() {
context.clearRect(0, 0, canvas.width, canvas.height);
drawBackground();
player.x++;
player.y++;
playerMove();
}
This is the JSFiddle.
EDIT WITH THE CORRECT ANSWER
The error is in the position of the object "player". It is located outside of the canvas, width:500 height:500 and the "player" is in position x:700 y:800.
Changing the position of the player your copy will appear.
var player = {
x : 50,
y : 50
}
Here the jsfiddle example.

Canvas draw following the path

I want to do the following on a HTML5 canvas / or SVG:
Have a background path, move cursor over and draw (fill) the background path
After the user complete drawing have a callback function
My problem is that I dont have any idea how to check if the drawed line is following the path.
Can someone explain me how to do this or maybe give some tips?
http://jsbin.com/reguyuxawo/edit?html,js,console,output
function drawBgPath() {
context.beginPath();
context.moveTo(100, 20);
context.lineTo(200, 160);
context.quadraticCurveTo(230, 200, 250, 120);
context.bezierCurveTo(290, -40, 300, 200, 400, 150);
context.lineTo(500, 90);
context.lineWidth = 5;
context.strokeStyle = 'rgba(0,0,0,.2)';
context.stroke();
}
Create a hidden canvas that stores the origin path as question Canvas, lets say, as #q.
Draw the question on the #c.
When user about to draw, get the pixel value from question to see whether its on a line or not.
Decide the draw color by the info above.
var mousePressed = false;
var lastX, lastY;
var ctx;
var canvas = document.getElementById('c');
var context = canvas.getContext('2d');
var canvasq = document.getElementById('q');
var contextq = canvasq.getContext('2d');
canvas.width = 500;
canvas.height = 500;
canvasq.width = 500;
canvasq.height = 500;
$('#c').mousedown(function (e) {
mousePressed = true;
Draw(e.pageX - $(this).offset().left, e.pageY - $(this).offset().top, false);
});
$('#c').mousemove(function (e) {
if (mousePressed) {
Draw(e.pageX - $(this).offset().left, e.pageY - $(this).offset().top, true);
}
});
$('#c').mouseup(function (e) {
mousePressed = false;
});
$('#c').mouseleave(function (e) {
mousePressed = false;
});
function drawBgPath() {
contextq.beginPath();
contextq.moveTo(100, 20);
contextq.lineTo(200, 160);
contextq.quadraticCurveTo(230, 200, 250, 120);
contextq.bezierCurveTo(290, -40, 300, 200, 400, 150);
contextq.lineTo(500, 90);
contextq.lineWidth = 5;
contextq.strokeStyle = 'rgba(0,0,0,.2)';
contextq.stroke();
context.drawImage(canvasq, 0, 0);
}
function Draw(x, y, isDown) {
// If not integer, getImageData will get a 2x2 region.
x = Math.round(x);
y = Math.round(y);
if (isDown) {
var pixel = contextq.getImageData(x, y, 1, 1);
// If the canvas is not draw by line, the opacity value will be 0.
var color = (pixel.data[3] === 0) ? 'red' : 'purple';
context.beginPath();
context.strokeStyle = color;
context.lineWidth = 5;
context.lineJoin = "round";
context.moveTo(lastX, lastY);
context.lineTo(x, y);
context.closePath();
context.stroke();
}
lastX = x; lastY = y;
}
drawBgPath();
Draw();
#q {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<canvas id="c"></canvas>
<canvas id="q"></canvas>
Another way is:
Create 2 additional canvas, for answer and question.
When mouse down, draw the path on answer first.
Then compare answer canvas with question canvas.
Draw the compared answer on the canvas for show.
I'll just demo how it can be achieve here. You can clip the draw region to improve the performance.
It's somehow hard to decide whether the path is complete or not. But you can still:
Clip the answer image by question, then compare their pixel value one-by-one.
If pixel on question has color, total + 1, if both pixel have color and color is same, count + 1.
Check if count/total is over a specific threshold.
It may be slow if the image is large, so I'd prefer to only check it when user mouseup or click a check button.
I've also tried to use .toDataURL to compare their value by string, however, its too strict and can't let you have a threshold.
var mousePressed = false;
var lastX, lastY;
var ctx;
// Question part
var qCanvas = document.createElement('canvas');
var qContext = qCanvas.getContext('2d');
var aCanvas = document.createElement('canvas');
var aContext = aCanvas.getContext('2d');
var canvas = document.getElementById('c');
var context = canvas.getContext('2d');
canvas.width = 500;
canvas.height = 500;
qCanvas.width = 500;
qCanvas.height = 500;
aCanvas.width = 500;
aCanvas.height = 500;
$('#c').mousedown(function (e) {
mousePressed = true;
Draw(e.pageX - $(this).offset().left, e.pageY - $(this).offset().top, false);
});
$('#c').mousemove(function (e) {
if (mousePressed) {
Draw(e.pageX - $(this).offset().left, e.pageY - $(this).offset().top, true);
}
});
$('#c').mouseup(function (e) {
mousePressed = false;
});
$('#c').mouseleave(function (e) {
mousePressed = false;
});
function drawBgPath() {
qContext.beginPath();
qContext.moveTo(100, 20);
qContext.lineTo(200, 160);
qContext.quadraticCurveTo(230, 200, 250, 120);
qContext.bezierCurveTo(290, -40, 300, 200, 400, 150);
qContext.lineTo(500, 90);
qContext.lineWidth = 5;
qContext.strokeStyle = 'rgb(0,0,0)';
qContext.stroke();
// Draw Question on canvas
context.save();
context.globalAlpha = 0.2;
context.drawImage(qCanvas, 0, 0);
context.restore();
// Now fill the question with purple.
qContext.fillStyle = 'purple';
qContext.globalCompositeOperation = 'source-atop';
qContext.fillRect(0, 0, qCanvas.width, qCanvas.height);
}
function Draw(x, y, isDown) {
if (isDown) {
// First draw on answer canvas
aContext.beginPath();
aContext.strokeStyle = 'red';
console.log(x, y);
aContext.lineWidth = 5;
aContext.lineJoin = "round";
aContext.moveTo(lastX, lastY);
aContext.lineTo(x, y);
aContext.closePath();
aContext.stroke();
// Compare answer with question.
aContext.save();
aContext.globalCompositeOperation = 'source-atop';
aContext.drawImage(qCanvas, 0, 0);
aContext.restore();
// Draw the result on what you want to show.
context.drawImage(aCanvas, 0, 0);
}
lastX = x; lastY = y;
}
var cv = document.createElement('canvas');
cv.width = 500;
cv.height = 500;
//document.body.appendChild(cv);
var ctx = cv.getContext('2d');
function checkAnswer() {
cv.width = 500;
cv.height = 500;
ctx.globalCompositeOperation = 'source-over';
ctx.drawImage(aCanvas, 0, 0);
ctx.globalCompositeOperation = 'destination-in';
ctx.drawImage(qCanvas, 0, 0);
var qData = qContext.getImageData(0, 0, 500, 500).data;
var aData = ctx.getImageData(0, 0, 500, 500).data;
var idx = 0, i, j;
var count = 0, total = 0;
for (i = 0; i < 500; ++i) {
for (j = 0; j < 500; ++j) {
if (qData[idx] !== 0) {
++total;
if (aData[idx] === qData[idx]) {
++count;
}
}
idx += 4;
}
}
console.log(count,total);
// Threshold.
if (count/total > 0.95) {
alert('Complete');
}
}
drawBgPath();
Draw();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<canvas id="c"></canvas>
<button onclick="checkAnswer()">check</button>

How to make a scrolling starfield

I want to write a simple scrolling right to left starfield. I have printed out the stars randomly. Now, how do I target each star and randomly give it a speed (say 1-10) and begin moving it? I also need to put each star back on the right edge after it reaches the left edge.
Following is my code written so far:
<!DOCTYPE html>
<html>
<head>
<script>
function stars()
{
canvas = document.getElementById("can");
if(canvas.getContext)
{
ctx = canvas.getContext("2d");
ctx.fillStyle = "black";
ctx.rect (0, 0, 400, 400);
ctx.fill();
starfield();
}
}
//print random stars
function starfield()
{
for (i=0; i<10; i++)
{
var x = Math.floor(Math.random()*399);
var y = Math.floor(Math.random()*399);
var tempx = x;
ctx.fillStyle = "white";
ctx.beginPath();
ctx.arc(x, y, 3, 0, Math.PI*2, true);
ctx.closePath();
ctx.fill();
}
}
</script>
</head>
<body onload="stars()">
<h1>Stars</h1>
<canvas id="can" width="400" height="400"style="border:2px solid #000100" ></canvas>
</body >
</html>
Here's a quick demo on Codepen. After saving the stars in an array, I'm using requestAnimationFrame to run the drawing code and update the position on every frame.
function stars() {
canvas = document.getElementById("can");
console.log(canvas);
if (canvas.getContext) {
ctx = canvas.getContext("2d");
ctx.fillStyle = "black";
ctx.rect(0, 0, 400, 400);
ctx.fill();
starfield();
}
}
// Create random stars with random velocity.
var starList = []
function starfield() {
for (i = 0; i < 20; i++) {
var star = {
x: Math.floor(Math.random() * 399),
y: Math.floor(Math.random() * 399),
vx: Math.ceil(Math.random() * 10)
};
starList.push(star);
}
}
function run() {
// Register for the next frame
window.requestAnimationFrame(run);
// Reset the canvas
ctx.fillStyle = "black";
ctx.rect(0, 0, 400, 400);
ctx.fill();
// Update position and draw each star.
var star;
for(var i=0, j=starList.length; i<j; i++) {
star = starList[i];
star.x = (star.x - star.vx + 400) % 400;
ctx.fillStyle = "white";
ctx.beginPath();
ctx.arc(star.x, star.y, 3, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fill();
}
}
stars();
run();
Put your x,y coordinates in an array, and then make a function that draws the array.
var stars = [
{x:110, y:80},
{x:120, y:20},
{x:130, y:60},
{x:140, y:40}
]
Then make a function to alter the x,y coordinates (for example increment y=y+1) each time before using the draw function.
Bonus:
This array solution allows you to have each star move at its own speed, you could store a delta (say 1 upto 3) in that array, and do y=y+delta instead. This looks 3D.
You could even go further and have a seperate x and y delta, and have stars fly out from the middle, which is even more 3D!
Or even simpler/faster could be to have the render function accept an x,y offset. It could then even wrap around, so that what falls off the screen on one side comes back on the other. It looks like you are rotating in space.
I simple way to imitate star movement towards a point(like a center) is simply divide both X and Y by Z coordinate.
nx = x / z
ny = y / z
And simply decrease z value as you iterate. As z is big, your points will be around a point and as z decreases the result will be bigger and bigger which imitates "moving" of a stars.
Just providing a solution which uses jQuery because using it you can get the output with lesser lines of code compared to complete canvas solution.It uses two canvas divs to get the desired output:
Check this fiddle
Little updated code from the code posted in the question
<script>
function stars(){
canvas = document.getElementById("can1");
canvasCopy = document.getElementById("can2");
if(canvas.getContext){
ctx = canvas.getContext("2d");
ctx.fillStyle = "black";
ctx.rect (0, 0, 400, 400);
ctx.fill();
starfield();
var destCtx = canvasCopy.getContext('2d');
destCtx.drawImage(canvas, 0, 0);
}
}
//print random stars
function starfield(){
for (i=0;i<10;i++){
var x = Math.floor(Math.random()*399);
var y = Math.floor(Math.random()*399);
var tempx = x;
ctx.fillStyle = "white";
ctx.beginPath();
ctx.arc(x, y, 3, 0, Math.PI*2, true);
ctx.closePath();
ctx.fill();
}
}
</script>
<body onload="stars()">
<h1>Stars</h1>
<div id="starBlocks">
<canvas id="can1" width="400" height="400"style="border:2px solid #000100" ></canvas>
<canvas id="can2" width="400" height="400"style="border:2px solid #000100" ></canvas>
</div>
</body >
jQuery
function playStars()
{
$('#starBlocks').animate({
scrollLeft : 400
},10000,'linear',function(){
$('#starBlocks').scrollLeft(0);
playStars();
});
}
playStars();
CSS
#starBlocks{
white-space:nowrap;
font-size:0px;
width:400px;
overflow:hidden;
}

Animation loop and scaling

Well I've got a few question to ask! Firstly What this code is doing is creating and drawing snowflakes with unique density which will all fall at a different rate. My first question is how do i make this loop continuous?
Secondly, I've translated my origin point(0,0) to the middle of the canvas (it was part of the criteria). I've now got this issue in which that when the snowfall is called it will either be drawn on the left side of the screen or the right, not both. How do i solve this?
Finally i know when doing animations that you have to clear the canvas after each re-drawing, however i haven't added this in and yet it still works fine?
//Check to see if the browser supports
//the addEventListener function
if(window.addEventListener)
{
window.addEventListener
(
'load', //this is the load event
onLoad, //this is the evemnt handler we going to write
false //useCapture boolen value
);
}
//the window load event handler
function onLoad(Xi, Yy) {
var canvas, context,treeObj, H, W, mp;
Xi = 0;
Yy = 0;
mp = 100;
canvas = document.getElementById('canvas');
context = canvas.getContext('2d');
W = window.innerWidth;
H = window.innerHeight;
canvas.width = W;
canvas.height = H;
context.translate(W/2, H/2);
var particles = [];
for(var i = 0; i < mp; i++) {
particles.push({
x: Math.random()*-W, //x
y: Math.random()*-H, //y
r: Math.random()*6+2, //radius
d: Math.random()* mp // density
})
}
treeObj = new Array();
var tree = new TTree(Xi, Yy);
treeObj.push(tree);
function drawCenterPot(){
context.beginPath();
context.lineWidth = "1";
context.strokeStyle = "Red";
context.moveTo(0,0);
context.lineTo(0,-H);
context.lineTo(0, H);
context.lineTo(-W, 0);
context.lineTo(W,0);
context.stroke();
context.closePath();
}
function drawMountain() {
context.beginPath();
context.fillStyle = "#FFFAF0";
context.lineWidth = "10";
context.strokeStyle = "Black";
context.moveTo(H,W);
context.bezierCurveTo(-H*10,W,H,W,H,W);
context.stroke();
context.fill();
}
function drawSky() {
var linearGrad = context.createLinearGradient(-100,-300, W/2,H);
linearGrad.addColorStop(0, "#000000");
linearGrad.addColorStop(1, "#004CB3");
context.beginPath();
context.fillStyle = linearGrad;
context.fillRect(-W/2, -H/2, W, H);
context.stroke();
context.fill();
drawMountain();
drawCenterPot();
}
function drawSnow(){
context.fillStyle = "White";
context.beginPath();
for(i = 0; i<mp; i++)
{
var p = particles[i];
context.moveTo(p.x,p.y);
context.arc(p.x, p.y, p.r, Math.PI*2, false);
}
context.fill();
}
function update() {
var angle = 0;
angle+=0.1;
for(var i=0; i<mp; i++) {
var p = particles[i];
p.x += Math.sin(angle) * 2;
p.y += Math.cos(angle+p.d) + 1 * p.r;
}
drawSky();
drawSnow();
draw();
}
function draw() {
for(var i =0; i < treeObj.length; i++)
{
context.save();
context.translate(Xi-H,Yy-W);
context.scale(1, 1);
treeObj[0].draw(context);
context.restore();
}
}
setInterval(update, 33);
}
About your animation:
What's happening is your flakes are falling out of view below the bottom of the canvas.
So when any flake's p.y+p.r > canvas.height you could:
destroy that flake and (optionally) add another falling from above the canvas
or
"recycle" that flake by changing its p.y to above the canvas.
About your design working without context.clearRect:
In your design, when you fill the whole canvas with "sky", you are effectively clearing the canvas.
About your flakes only falling on half the screen:
Instead of translating to mid-screen:
Don't translate at all and let p.x be any Math.random()*canvas.width

How to move rectangle on canvas

I use canvas in my application using JavaScript. On that canvas I draw one rectangle. I want to move rectangle with the help of mouse (e.g moving slider) how to move that rectangle using JavaScript or jQuery.
A Canvas is literally just a surface that you paint on and none of the things you paint are objects.
If you want to pretend they are objects (like moving around a rectangle or a line) then you need to keep track of everything and do all the hit-testing and re-painting yourself .
I wrote a gentle introduction article on getting started by making rectangles that you can select and drag around. Give that a read.
On a second reading, I think I misunderstood your question, so here's an updated version:
http://jsfiddle.net/HSMfR/4/
$(function () {
var
$canvas = $('#canvas'),
ctx = $canvas[0].getContext('2d'),
offset = $canvas.offset(),
draw,
handle;
handle = {
color: '#666',
dim: { w: 20, h: canvas.height },
pos: { x: 0, y: 0 }
};
$canvas.on({
'mousedown.slider': function (evt) {
var grabOffset = {
x: evt.pageX - offset.left - handle.pos.x,
y: evt.pageY - offset.top - handle.pos.y
};
// simple hit test
if ( grabOffset.x >= 0
&& grabOffset.x <= handle.dim.w
&& grabOffset.y >= 0
&& grabOffset.x <= handle.dim.h
) {
$(document).on({
'mousemove.slider': function (evt) {
handle.pos.x = evt.pageX - offset.left - grabOffset.x;
// prevent dragging out of canvas
if (handle.pos.x < 0) {
handle.pos.x = 0;
}
if (handle.pos.x + handle.dim.w > canvas.width) {
handle.pos.x = canvas.width - handle.dim.w;
}
//handle.pos.y = evt.pageY - offset.top - grabOffset.y;
},
'mouseup.slider': function () {
$(document).off('.slider');
}
});
}
}
});
draw = function() {
var val = (100 * (handle.pos.x / (canvas.width - handle.dim.w))).toFixed(2) + '%';
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.fillStyle = handle.color;
ctx.fillRect(handle.pos.x, handle.pos.y, handle.dim.w, handle.dim.h);
ctx.textBaseline = 'hanging';
ctx.font = '12px Verdana';
ctx.fillStyle = '#333';
ctx.fillText(val, 4, 4);
ctx.fillStyle = '#fff';
ctx.fillText(val, 3, 3);
};
setInterval(draw, 16);
});
prev version:
Very simple solution to extend upon:
http://jsfiddle.net/HSMfR/
$(function () {
var
ctx = $('#canvas')[0].getContext('2d'),
$pos = $('#pos'),
draw;
draw = function() {
var x = ($pos.val() / 100) * (ctx.canvas.width - 20);
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.fillStyle = 'black';
ctx.fillRect(x, 0, 20, 20);
};
setInterval(draw, 40);
});

Categories

Resources