I've got a task to play a little with JavaScript canvas. I have to onclick rotate the blue block with the movie, and as you can see in the fiddle it works, but the problem is my name which is below the film - it shouldn't rotate but stay on its position at the bottom of canvas (before clicking it's where it should be).
This is the fiddle (without the movie, but it shows the problem):
https://jsfiddle.net/463h8se7/
And this is my JavaScript (it is also in the fiddle, but for clarity here too):
var video;
var canvas;
var ctx;
var click = 0;
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
printName();
ctx.save();
ctx.fillStyle = "#558899";
ctx.fillRect(canvas.width / 4 - 5, canvas.height / 4 - 5, canvas.width / 2 + 10, canvas.height / 2 + 10);
ctx.drawImage(video, canvas.width / 4, canvas.height / 4, canvas.width / 2, canvas.height / 2);
requestAnimationFrame(animate);
}
function rotate() {
if (click == 0) {
click = 1;
i = window.setInterval(function () {
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate(2 * Math.PI / 180);
ctx.translate(-canvas.width / 2, -canvas.height / 2);
}, 20);
} else {
click = 0;
window.clearInterval(i);
}
}
function printName() {
ctx.fillStyle = "black";
ctx.font = "15px Arial";
ctx.fillText("aceimnors", canvas.width / 3, canvas.height - 1);
}
function load() {
video = document.getElementById("video");
canvas = document.getElementById("canvas");
ctx = canvas.getContext('2d');
animate();
}
<div id="div_canvas">
<canvas id="canvas" onclick="rotate()"></canvas>
<p>canvas</p>
</div>
I would just put it in animate, since you are already redrawing every requestAnimationFrame.
Keep an angle variable and remember to rotate back.
var video = document.body.appendChild(document.createElement("img"));
video.id = "video";
video.addEventListener("load", load);
video.style.display = "none";
video.src = "https://www.w3schools.com/tags/img_girl.jpg";
var canvas = document.body.appendChild(document.createElement("canvas"));
canvas.id = "canvas";
var ctx = canvas.getContext("2d");
var click = false;
var angle = 0;
var angleTarget = 0;
canvas.addEventListener("click", function () { return angleTarget += .1; });
function animate() {
//Compund Angle
if (angle < angleTarget) {
angle += 0.01;
}
if (angle > angleTarget) {
angle = angleTarget;
}
//Clear
ctx.clearRect(0, 0, canvas.width, canvas.height);
//Text
printName();
//Rotate
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate(angle * (Math.PI * 2));
ctx.translate(-canvas.width / 2, -canvas.height / 2);
//Draw Graphic
ctx.fillStyle = "#558899";
ctx.fillRect(canvas.width / 4 - 5, canvas.height / 4 - 5, canvas.width / 2 + 10, canvas.height / 2 + 10);
ctx.drawImage(video, canvas.width / 4, canvas.height / 4, canvas.width / 2, canvas.height / 2);
requestAnimationFrame(animate);
//Rotate back
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate(-angle * (Math.PI * 2));
ctx.translate(-canvas.width / 2, -canvas.height / 2);
}
function printName() {
ctx.fillStyle = "black";
ctx.font = "15px Arial";
ctx.fillText("aceimnors", canvas.width / 3, canvas.height - 1);
}
function load() {
video = document.getElementById("video");
canvas = document.getElementById("canvas");
ctx = canvas.getContext('2d');
animate();
}
If you check out my DrawingBox it will give you an idea of how to rotate. You can start reading code at // magic under here:
//<![CDATA[
/* js/external.js */
let get, post,doc, htm, bod, nav, M, I, mobile, beacon, S, Q, hC, aC, rC, tC, inArray, shuffle, isNum, isInt, rand, DrawingBox;
addEventListener('load', ()=>{
get = (url, func, responseType = 'json', context = null)=>{
const x = new XMLHttpRequest;
const c = context || x;
x.open('GET', url); x.responseType = responseType;
x.onload = ()=>{
if(func)func.call(c, x.response);
}
x.onerror = e=>{
if(func)func.call(c, {xhrErrorEvent:e});
}
x.send();
return x;
}
post = function(url, send, func, responseType ='json', context = null){
const x = new XMLHttpRequest;
if(typeof send === 'object' && send && !(send instanceof Array)){
const c = context || x;
x.open('POST', url); x.responseType = responseType;
x.onload = ()=>{
if(func)func.call(c, x.response);
}
x.onerror = e=>{
if(func)func.call(c, {xhrErrorEvent:e});
}
let d;
if(send instanceof FormData){
d = send;
}
else{
let s;
d = new FormData;
for(let k in send){
s = send[k];
if(typeof s === 'object' && s)s = JSON.stringify(s);
d.append(k, s);
}
}
x.send(d);
}
else{
throw new Error('send argument must be an Object');
}
return x;
}
doc = document; htm = doc.documentElement; bod = doc.body; nav = navigator; M = tag=>doc.createElement(tag); I = id=>doc.getElementById(id);
mobile = nav.userAgent.match(/Mobi/i) ? true : false;
beacon = function(url, send){
let r = false;
if(typeof send === 'object' && send && !(send instanceof Array)){
let d;
if(send instanceof FormData){
d = send;
}
else{
let s;
d = new FormData;
for(let k in send){
s = send[k];
if(typeof s === 'object' && s)s = JSON.stringify(s);
d.append(k, s);
}
}
r = nav.sendBeacon(url, d);
}
else{
throw new Error('send argument must be an Object');
}
return r;
}
S = (selector, within)=>{
var w = within || doc;
return w.querySelector(selector);
}
Q = (selector, within)=>{
var w = within || doc;
return w.querySelectorAll(selector);
}
hC = function(node, className){
return node.classList.contains(className);
}
aC = function(){
const a = [...arguments];
a.shift().classList.add(...a);
return aC;
}
rC = function(){
const a = [...arguments];
a.shift().classList.remove(...a);
return rC;
}
tC = function(){
const a = [...arguments];
a.shift().classList.toggle(...a);
return tC;
}
inArray = (mixed, array)=>{
if(array.indexOf(mixed) === -1){
return false;
}
return true;
}
shuffle = array=>{
let a = array.slice(), i = a.length, n, h;
while(i){
n = Math.floor(Math.random()*i--); h = a[i]; a[i] = a[n]; a[n] = h;
}
return a;
}
isNum = mixed=>typeof mixed === 'number' && !isNaN(mixed); isInt = mixed=>Number.isInteger(mixed);
rand = (min, max)=>{
let mn = min, mx = max;
if(mx === undefined){
mx = mn; mn = 0;
}
return mn+Math.floor(Math.random()*(mx-mn+1));
}
DrawingBox = function(canvas, width = null, height = null){
this.canvas; this.ctx;
this.setCanvas = (canvas, width = null, height = null)=>{
canvas.width = width || innerWidth;
canvas.height = height || innerHeight; this.canvas = canvas;
this.ctx = canvas.getContext('2d');
return this;
}
this.point = (x, y)=>{
const c = this.ctx;
c.beginPath(); c.moveTo(x, y);
return this;
}
this.rotate = (x, y, degrees, drawFunc)=>{
const c = this.ctx;
c.save(); c.translate(x, y); c.rotate(degrees*Math.PI/180); c.translate(-x, -y);
drawFunc(c, x, y); c.restore();
return this;
}
this.setCanvas(canvas, width, height);
}
// magic under here
const box = new DrawingBox(I('can')), ctx = box.ctx;
ctx.fillStyle = '#c00'; ctx.fillRect(0, 0, 100, 75);
box.rotate(100, 75, 45, (c, x, y)=>{
c.fillStyle = '#00c'; c.fillRect(x, y, 100, 50);
});
ctx.strokeStyle = '#0c0'; ctx.strokeRect(100, 75, 35, 82);
}); // end load
/* css/external.css */
*{
box-sizing:border-box; color:#000; padding:0; margin:0; overflow:hidden;
}
html,body{
width:100%; height:100%;
}
#can{
background:#fff;
}
<!DOCTYPE html>
<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>
<head>
<meta charset='UTF-8' /><meta name='viewport' content='width=device-width, height=device-height, initial-scale:1, user-scalable=no' />
<title>Title Here</title>
<link type='text/css' rel='stylesheet' href='css/external.css' />
<script src='js/external.js'></script>
</head>
<body>
<canvas id='can'></canvas>
</body>
</html>
You'll notice the blue box is rotated. The DrawingBoxInstance.rotate method takes x,y origin and rotation degrees.
Not that I'm showing you this here, but I will tell you that the real secret of creating user generated animations or drawings (or games) is to push an Object, for every operation, onto an Array can be passed back into your drawing functions. Of course, you will be canvasContext.clearRect(0, 0, canvasWidth, canvasHeight)ing before redrawing previous states to create the illusion of movement.
Related
i made a simple script for color a pixel and all the near pixel with the same color
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="http://code.jquery.com/jquery-latest.js"></script>
<title>
Click foto
</title>
<style type="text/css">
/*<![CDATA[*/
html, body{
height: 100%;
}
/*]]>*/
</style>
</head>
<body>
<div id="canvasDiv">
</div>
</body>
<script>
var canvasDiv = document.getElementById('canvasDiv');
canvas = document.createElement('canvas');
canvas.setAttribute('width', 500);
canvas.setAttribute('height', 500);
canvas.setAttribute('id', 'canvas');
$(canvasDiv).prepend(canvas);
if(typeof G_vmlCanvasManager != 'undefined') {
canvas = G_vmlCanvasManager.initElement(canvas);
}
var context = canvas.getContext('2d');
var imageObj = new Image();
imageObj.onload = function() {
$(canvas).attr({width : this.width, height: this.height});
context.drawImage(imageObj,0,0);
};
imageObj.src = 'cartina_italia.png';
$('#canvas').click(function(e){
console.time('click');
mouseX = e.pageX - this.offsetLeft;
mouseY = e.pageY - this.offsetTop;
c = this.getContext('2d');
p = c.getImageData(mouseX, mouseY, 1, 1).data;
hex = ("000000" + rgbToHex(p[0], p[1], p[2])).slice(-6);
console.timeEnd('click');
console.time('selectArea');
selectArea(mouseX,mouseY,c,hex);
console.timeEnd('selectArea');
});
function selectArea(x,y,c,color){
if (x>=0 && y>=0){
p = c.getImageData(x, y, 1, 1).data;
hex =("000000" + rgbToHex(p[0], p[1], p[2])).slice(-6);
if (color==hex){
c.fillStyle = "rgba(255,0,0,0.1)";
c.fillRect( x, y, 1, 1 );
selectArea(x+1,y,c,color);
selectArea(x-1,y,c,color);
selectArea(x,y+1,c,color);
selectArea(x,y-1,c,color);
}
}
}
function rgbToHex(r, g, b) {
if (r > 255 || g > 255 || b > 255)
throw "Invalid color component";
return ((r << 16) | (g << 8) | b).toString(16);
}
</script>
</html>
i'm using for test this file: http://mappa.italiachecambia.org/assets/homemap/cartina_italia.png the first 3 times i click on a region for color it i have a slow response(1000-5000ms), after the first 3 times the function end in 50ms
i can't use jsfiddle for show the problem bc i get cross-origin error
the code is simple recursive function that change the color of the clicked pixel and launched on the near pixel untill the pixel color is different from the first
but i don't understand why the first 3 times have this slow response and after the 4th is with 0 lag....
You should cache the imageData of the hole image and work with that data instead of invoking getImageData on each selectArea call. Also you could think of implementing it iterative, to prevent maximum call stack errors.
Here's an example:
var ExtendedCanvas = (function() {
var context, data, canvas;
function ExtendedCanvas(selector, imageSrc) {
var wrapper = document.querySelector(selector);
this.element = canvas = document.createElement('canvas');
context = this.element.getContext('2d');
loadImage.call(this, imageSrc, function(image) {
canvas.setAttribute('width', image.width);
canvas.setAttribute('height', image.height);
context.drawImage(image,0,0);
data = context.getImageData(0,0,canvas.width, canvas.height);
});
wrapper.appendChild(this.element);
}
function loadImage(src, cb) {
var image = new Image();
var canvas = this.element;
image.onload = function() {
cb(this);
}
image.crossOrigin = 'Anonymous';
image.src = src;
}
ExtendedCanvas.prototype.getPixelIndex = function(x, y) {
return (Math.floor(y) * canvas.width + Math.floor(x)) * 4;
}
ExtendedCanvas.prototype.getPixelColor = function(x, y) {
var index = this.getPixelIndex(x, y);
var d = data.data;
var r = d[index];
var g = d[index + 1];
var b = d[index + 2];
var a = d[index + 3];
return [r, g, b, a];
}
ExtendedCanvas.prototype.setPixelColor = function(x, y, color) {
var index = this.getPixelIndex(x, y);
var d = data.data;
d[index] = color[0];
d[index + 1] = color[1];
d[index + 2] = color[2];
d[index + 3] = color[3];
}
ExtendedCanvas.prototype.fill = function(x, y, fillColor) {
if(x < 0 || y < 0 || x > canvas.width || y > canvas.height) {
return;
}
fillColor = fillColor || [0,0,0,255];
var stack = [];
var color = this.getPixelColor(x, y).join();
if(color === fillColor) {
return;
}
stack.push([x, y]);
context.fillStyle = fillColor;
if(color === fillColor.join()) {
return;
}
while(stack.length > 0) {
var position = stack.pop();
var posX = position[0];
var posY = position[1];
var posColor = this.getPixelColor(posX, posY).join();
if(posColor === color) {
this.setPixelColor(posX, posY, fillColor);
stack.push([posX, posY + 1]);
stack.push([posX, posY - 1]);
stack.push([posX + 1, posY]);
stack.push([posX - 1, posY]);
}
}
context.putImageData(data, 0, 0);
}
return ExtendedCanvas;
})();
document.addEventListener('DOMContentLoaded', function() {
var c = new ExtendedCanvas('#canvasWrapper', 'https://i.imgur.com/QWaKVGO.png');
c.element.addEventListener('click', function(e) {
var x = e.pageX - this.offsetLeft;
var y = e.pageY - this.offsetTop;
c.fill(x, y);
});
});
<div id="canvasWrapper"></div>
I'm trying to use Javascript to create a Canvas Game which is similar to Bug Smasher or Ant Smasher game. It means the obj will move randomly, and when u click on it, the score will increase. ( I'm not allowed to use JQuery )
I almost finished all the work. But there's an error I cannot figure out: Everytime I click on the object, the score increases but it overwrites the number "0" like this:
And this is my code:
// Create the canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext('2d');
var timer = 0;
var caught = false;
var fps = 10;
document.body.appendChild(canvas);
canvas.width = 800;
canvas.height = 544;
// Background image
var bgReady = false;
var bgImage = new Image();
bgImage.onload = function () {
bgReady = true;
};
bgImage.src = "images/background.png";
// nar image
var narReady = false;
var narImage = new Image();
narImage.onload = function () {
narReady = true;
};
narImage.src = "images/nar.png";
var nar = {};
var narCaught = 0;
// When nar is caught, reset
var reset = function () {
nar.x = 40 + (Math.random() * (canvas.width - 70));
do {
nar.y = 40 + (Math.random() * (canvas.height - 70));
}
while (nar.y < 100)
};
//mousedown event
window.addEventListener("mousedown", onMouseDown, false);
function onMouseDown(e) {
if (e.button != 0) return;
mouseXinCanvas = e.clientX;
mouseYinCanvas = e.clientY;
if (narBody(nar, mouseXinCanvas, mouseYinCanvas)) {
caught = true;
clearInterval(timer);
timer = setInterval(reset, 20000 / fps);
reset();
}
if (ResetScore(mouseXinCanvas, mouseYinCanvas)) {
location.reload();
}
if (ResetSpeed(mouseXinCanvas, mouseYinCanvas)) {
clearInterval(timer);
timer = setInterval(reset, 20000 / fps);
reset();
render();
}
};
//nar's body define
function narBody(nar, x, y) {
if (x <= (nar.x + 80)
&& nar.x <= (x + 80)
&& y <= (nar.y + 80)
&& nar.y <= (y + 80)
) {
fps = fps + 5;
narCaught++;
return true;
}
return false;
};
//Reset Score box
function ResetScore(x, y) {
if (x > (305)
&& x < (545)
&& y > (15)
&& y < (85)
) {
return true;
}
return false;
};
//Reset speed box
function ResetSpeed(x, y) {
if (x > (605)
&& x < (845)
&& y > (15)
&& y < (85)
) {
fps = 10;
return true;
}
return false;
};
// Draw everything
var render = function () {
if (bgReady) {
ctx.drawImage(bgImage, 0, 100);
}
if (narReady) {
ctx.drawImage(narImage, nar.x, nar.y);
}
if (caught == true) {
if (bgReady) {
ctx.drawImage(bgImage, 0, 100);
}
caught = false;
}
// Score, Title
ctx.fillStyle = "rgb(65, 226, 24)";
ctx.font = "34px Helvetica";
ctx.textAlign = "left";
ctx.textBaseline = "top";
ctx.fillText("Catch Naruto!!!", 5, 40);
ctx.font = "20px Helvetica";
ctx.fillText("Score: " + narCaught, 10, 10);
// Reset Score, Speed button
ctx.fillStyle = "rgb(30, 168, 99)";
ctx.fillRect(250, 10, 250, 80);
ctx.fillRect(520, 10, 250, 80);
ctx.fillStyle = "rgb(30, 168, 99)";
ctx.fillRect(255, 15, 240, 70);
ctx.fillRect(525, 15, 240, 70);
ctx.fillStyle = "rgb(255, 255, 255)";
ctx.font = "34px Arial";
ctx.fillText("Reset Score", 275, 30);
ctx.fillText("Reset Speed", 545, 30);
};
// The main game loop
var main = function () {
render();
// Request to do this again ASAP
requestAnimationFrame(main);
};
// Cross-browser support for requestAnimationFrame
var w = window;
requestAnimationFrame = w.requestAnimationFrame || w.webkitRequestAnimationFrame || w.msRequestAnimationFrame || w.mozRequestAnimationFrame;
// Let's play this game!
//var then = Date.now();
reset();
main();
<html lang="en">
<head>
<meta charset="utf-8">
<title>Assignment 5</title>
</head>
<body>
<script src="game.js"></script>
</body>
</html>
Please help ! :(
The problem is simple to solve. You just need to clear all the previous frame's rendering before you render the new frame.
Just add the following line to the render function
ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height);
You will notice that the quality of the text also improves. This is because you are no long drawing over the top of the old text,
I dont know what the background image is so I clear the whole canvas. But if the background image is not transparent you only need to clear what it does not cover.
See change below
// Create the canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext('2d');
var timer = 0;
var caught = false;
var fps = 10;
document.body.appendChild(canvas);
canvas.width = 800;
canvas.height = 544;
// Background image
var bgReady = false;
var bgImage = new Image();
bgImage.onload = function () {
bgReady = true;
};
bgImage.src = "images/background.png";
// nar image
var narReady = false;
var narImage = new Image();
narImage.onload = function () {
narReady = true;
};
narImage.src = "images/nar.png";
var nar = {};
var narCaught = 0;
// When nar is caught, reset
var reset = function () {
nar.x = 40 + (Math.random() * (canvas.width - 70));
do {
nar.y = 40 + (Math.random() * (canvas.height - 70));
}
while (nar.y < 100)
};
//mousedown event
window.addEventListener("mousedown", onMouseDown, false);
function onMouseDown(e) {
if (e.button != 0) return;
mouseXinCanvas = e.clientX;
mouseYinCanvas = e.clientY;
if (narBody(nar, mouseXinCanvas, mouseYinCanvas)) {
caught = true;
clearInterval(timer);
timer = setInterval(reset, 20000 / fps);
reset();
}
if (ResetScore(mouseXinCanvas, mouseYinCanvas)) {
location.reload();
}
if (ResetSpeed(mouseXinCanvas, mouseYinCanvas)) {
clearInterval(timer);
timer = setInterval(reset, 20000 / fps);
reset();
render();
}
};
//nar's body define
function narBody(nar, x, y) {
if (x <= (nar.x + 80)
&& nar.x <= (x + 80)
&& y <= (nar.y + 80)
&& nar.y <= (y + 80)
) {
fps = fps + 5;
narCaught++;
return true;
}
return false;
};
//Reset Score box
function ResetScore(x, y) {
if (x > (305)
&& x < (545)
&& y > (15)
&& y < (85)
) {
return true;
}
return false;
};
//Reset speed box
function ResetSpeed(x, y) {
if (x > (605)
&& x < (845)
&& y > (15)
&& y < (85)
) {
fps = 10;
return true;
}
return false;
};
// Draw everything
var render = function () {
//===========================================================
// add the following line to clear the display.
ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height);
if (bgReady) {
ctx.drawImage(bgImage, 0, 100);
}
if (narReady) {
ctx.drawImage(narImage, nar.x, nar.y);
}
if (caught == true) {
if (bgReady) {
ctx.drawImage(bgImage, 0, 100);
}
caught = false;
}
// Score, Title
ctx.fillStyle = "rgb(65, 226, 24)";
ctx.font = "34px Helvetica";
ctx.textAlign = "left";
ctx.textBaseline = "top";
ctx.fillText("Catch Naruto!!!", 5, 40);
ctx.font = "20px Helvetica";
ctx.fillText("Score: " + narCaught, 10, 10);
// Reset Score, Speed button
ctx.fillStyle = "rgb(30, 168, 99)";
ctx.fillRect(250, 10, 250, 80);
ctx.fillRect(520, 10, 250, 80);
ctx.fillStyle = "rgb(30, 168, 99)";
ctx.fillRect(255, 15, 240, 70);
ctx.fillRect(525, 15, 240, 70);
ctx.fillStyle = "rgb(255, 255, 255)";
ctx.font = "34px Arial";
ctx.fillText("Reset Score", 275, 30);
ctx.fillText("Reset Speed", 545, 30);
};
// The main game loop
var main = function () {
render();
// Request to do this again ASAP
requestAnimationFrame(main);
};
// Cross-browser support for requestAnimationFrame
var w = window;
requestAnimationFrame = w.requestAnimationFrame || w.webkitRequestAnimationFrame || w.msRequestAnimationFrame || w.mozRequestAnimationFrame;
// Let's play this game!
//var then = Date.now();
reset();
main();
I am designing a paint bucket app. My code is working fine. Just need a little help. On zoom in and zoom out buttons, I am changing the height and width of canvas. How can I apply animation to it? This is my complete code. The zoom part is at the end. You can simply copy paste it inside a file. It will work.
<!DOCTYPE html>
<html>
<head>
<title>Painitng</title>
<style>
body {
width: 100%;
height: auto;
text-align: center;
}
.colorpick {
widh: 100%;
height: atuo;
}
.pick {
display: inline-block;
width: 30px;
height: 30px;
margin: 5px;
cursor: pointer;
}
canvas {
border: 2px solid silver;
}
</style>
</head>
<body>
<button id="zoomin">Zoom In</button>
<button id="zoomout">Zoom Out</button>
<button id = "undo-button" onclick="history('undo')">Undo</button>
<button id = "redo-button" onclick="history('redo')">Redo</button>
<div id="canvasDiv"></div>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.js"></script>
<script type="text/javascript">
var colorYellow = {
r: 255,
g: 207,
b: 51
};
var context;
var canvasWidth = 500;
var canvasHeight = 500;
var myColor = colorYellow;
var curColor = myColor;
var outlineImage = new Image();
var backgroundImage = new Image();
var drawingAreaX = 0;
var drawingAreaY = 0;
var drawingAreaWidth = 500;
var drawingAreaHeight = 500;
var colorLayerData;
var outlineLayerData;
var totalLoadResources = 2;
var curLoadResNum = 0;
var undoarr = new Array();
var redoarr = new Array();
function history(command){ // handles undo/redo button events.
var data;
if(command === "redo"){
data = historyManager.redo(); // get data for redo
}else
if(command === "undo"){
data = historyManager.undo(); // get data for undo
}
if(data !== undefined){ // if data has been found
setColorLayer(data); // set the data
}
}
// sets colour layer and creates copy into colorLayerData
function setColorLayer(data){
context.putImageData(data, 0, 0);
colorLayerData = context.getImageData(0, 0, canvasWidth, canvasHeight);
context.drawImage(backgroundImage, 0, 0, canvasWidth, canvasHeight);
context.drawImage(outlineImage, 0, 0, drawingAreaWidth, drawingAreaHeight);
}
// Clears the canvas.
function clearCanvas() {
context.clearRect(0, 0, context.canvas.width, context.canvas.height);
}
// Draw the elements on the canvas
function redraw() {
uc = 0;
rc = 0;
var locX,
locY;
// Make sure required resources are loaded before redrawing
if (curLoadResNum < totalLoadResources) {
return; // To check if images are loaded successfully or not.
}
clearCanvas();
// Draw the current state of the color layer to the canvas
context.putImageData(colorLayerData, 0, 0);
historyManager.push(context.getImageData(0, 0, canvasWidth, canvasHeight));
redoarr = new Array();
// Draw the background
context.drawImage(backgroundImage, 0, 0, canvasWidth, canvasHeight);
// Draw the outline image on top of everything. We could move this to a separate
// canvas so we did not have to redraw this everyime.
context.drawImage(outlineImage, 0, 0, drawingAreaWidth, drawingAreaHeight);
}
;
function matchOutlineColor(r, g, b, a) {
return (r + g + b < 100 && a === 255);
}
;
function matchStartColor(pixelPos, startR, startG, startB) {
var r = outlineLayerData.data[pixelPos],
g = outlineLayerData.data[pixelPos + 1],
b = outlineLayerData.data[pixelPos + 2],
a = outlineLayerData.data[pixelPos + 3];
// If current pixel of the outline image is black
if (matchOutlineColor(r, g, b, a)) {
return false;
}
r = colorLayerData.data[pixelPos];
g = colorLayerData.data[pixelPos + 1];
b = colorLayerData.data[pixelPos + 2];
// If the current pixel matches the clicked color
if (r === startR && g === startG && b === startB) {
return true;
}
// If current pixel matches the new color
if (r === curColor.r && g === curColor.g && b === curColor.b) {
return false;
}
return true;
}
;
function colorPixel(pixelPos, r, g, b, a) {
colorLayerData.data[pixelPos] = r;
colorLayerData.data[pixelPos + 1] = g;
colorLayerData.data[pixelPos + 2] = b;
colorLayerData.data[pixelPos + 3] = a !== undefined ? a : 255;
}
;
function floodFill(startX, startY, startR, startG, startB) {
var newPos,
x,
y,
pixelPos,
reachLeft,
reachRight,
drawingBoundLeft = drawingAreaX,
drawingBoundTop = drawingAreaY,
drawingBoundRight = drawingAreaX + drawingAreaWidth - 1,
drawingBoundBottom = drawingAreaY + drawingAreaHeight - 1,
pixelStack = [[startX, startY]];
while (pixelStack.length) {
newPos = pixelStack.pop();
x = newPos[0];
y = newPos[1];
// Get current pixel position
pixelPos = (y * canvasWidth + x) * 4;
// Go up as long as the color matches and are inside the canvas
while (y >= drawingBoundTop && matchStartColor(pixelPos, startR, startG, startB)) {
y -= 1;
pixelPos -= canvasWidth * 4;
}
pixelPos += canvasWidth * 4;
y += 1;
reachLeft = false;
reachRight = false;
// Go down as long as the color matches and in inside the canvas
while (y <= drawingBoundBottom && matchStartColor(pixelPos, startR, startG, startB)) {
y += 1;
colorPixel(pixelPos, curColor.r, curColor.g, curColor.b);
if (x > drawingBoundLeft) {
if (matchStartColor(pixelPos - 4, startR, startG, startB)) {
if (!reachLeft) {
// Add pixel to stack
pixelStack.push([x - 1, y]);
reachLeft = true;
}
} else if (reachLeft) {
reachLeft = false;
}
}
if (x < drawingBoundRight) {
if (matchStartColor(pixelPos + 4, startR, startG, startB)) {
if (!reachRight) {
// Add pixel to stack
pixelStack.push([x + 1, y]);
reachRight = true;
}
} else if (reachRight) {
reachRight = false;
}
}
pixelPos += canvasWidth * 4;
}
}
}
;
// Start painting with paint bucket tool starting from pixel specified by startX and startY
function paintAt(startX, startY) {
var pixelPos = (startY * canvasWidth + startX) * 4,
r = colorLayerData.data[pixelPos],
g = colorLayerData.data[pixelPos + 1],
b = colorLayerData.data[pixelPos + 2],
a = colorLayerData.data[pixelPos + 3];
if (r === curColor.r && g === curColor.g && b === curColor.b) {
// Return because trying to fill with the same color
return;
}
if (matchOutlineColor(r, g, b, a)) {
// Return because clicked outline
return;
}
floodFill(startX, startY, r, g, b);
redraw();
}
;
// Add mouse event listeners to the canvas
function createMouseEvents() {
$('#canvas').mousedown(function (e) {
// Mouse down location
var mouseX = e.pageX - this.offsetLeft,
mouseY = e.pageY - this.offsetTop;
// assuming that the mouseX and mouseY are the mouse coords.
if(this.style.width){ // make sure there is a width in the style
// (assumes if width is there then height will be too
var w = Number(this.style.width.replace("px","")); // warning this will not work if size is not in pixels
var h = Number(this.style.height.replace("px","")); // convert the height to a number
var pixelW = this.width; // get the canvas resolution
var pixelH = this.height;
mouseX = Math.floor((mouseX / w) * pixelW); // convert the mouse coords to pixel coords
mouseY = Math.floor((mouseY / h) * pixelH);
}
if ((mouseY > drawingAreaY && mouseY < drawingAreaY + drawingAreaHeight) && (mouseX <= drawingAreaX + drawingAreaWidth)) {
paintAt(mouseX, mouseY);
}
});
}
;
resourceLoaded = function () {
curLoadResNum += 1;
//if (curLoadResNum === totalLoadResources) {
createMouseEvents();
redraw();
//}
};
var historyManager = (function (){ // Anon for private (closure) scope
var uBuffer = []; // this is undo buff
var rBuffer = []; // this is redo buff
var currentState = undefined; // this holds the current history state
var undoElement = undefined;
var redoElement = undefined;
var manager = {
UI : { // UI interface just for disable and enabling redo undo buttons
assignUndoButton : function(element){
undoElement = element;
this.update();
},
assignRedoButton : function(element){
redoElement = element;
this.update();
},
update : function(){
if(redoElement !== undefined){
redoElement.disabled = (rBuffer.length === 0);
}
if(undoElement !== undefined){
undoElement.disabled = (uBuffer.length === 0);
}
}
},
reset : function(){
uBuffer.length = 0;
rBuffer.length = 0;
currentState = undefined;
this.UI.update();
},
push : function(data){
if(currentState !== undefined){
var same=true
for(i=0;i<data.data.length;i++){
if(data.data[i] !== currentState.data[i]){
same = false;break;
}
} if(same){
return;
}
}
if(currentState !== undefined){
uBuffer.push(currentState);
}
currentState = data;
rBuffer.length = 0;
this.UI.update();
},
undo : function(){
if(uBuffer.length > 0){
if(currentState !== undefined){
rBuffer.push(currentState);
}
currentState = uBuffer.pop();
}
this.UI.update();
return currentState; // return data or unfefined
},
redo : function(){
if(rBuffer.length > 0){
if(currentState !== undefined){
uBuffer.push(currentState);
}
currentState = rBuffer.pop();
}
this.UI.update();
return currentState;
},
}
return manager;
})();
function start() {
var canvas = document.createElement('canvas');
canvas.setAttribute('width', canvasWidth);
canvas.setAttribute('height', canvasHeight);
canvas.setAttribute('id', 'canvas');
document.getElementById('canvasDiv').appendChild(canvas);
if (typeof G_vmlCanvasManager !== "undefined") {
canvas = G_vmlCanvasManager.initElement(canvas);
}
context = canvas.getContext("2d");
backgroundImage.onload = resourceLoaded();
backgroundImage.src = "images/t1.png";
outlineImage.onload = function () {
context.drawImage(outlineImage, drawingAreaX, drawingAreaY, drawingAreaWidth, drawingAreaHeight);
try {
outlineLayerData = context.getImageData(0, 0, canvasWidth, canvasHeight);
} catch (ex) {
window.alert("Application cannot be run locally. Please run on a server.");
return;
}
clearCanvas();
colorLayerData = context.getImageData(0, 0, canvasWidth, canvasHeight);
resourceLoaded();
};
outlineImage.src = "images/d.png";
}
;
if(historyManager !== undefined){
// only for visual feedback and not required for the history manager to function.
historyManager.UI.assignUndoButton(document.querySelector("#undo-button"));
historyManager.UI.assignRedoButton(document.querySelector("#redo-button"));
}
getColor = function () {
};
</script>
<script type="text/javascript"> $(document).ready(function () {
start();
});</script>
<script language="javascript">
$('#zoomin').click(function () {
if ($("#canvas").width()==500){
$("#canvas").width(750);
$("#canvas").height(750);
var ctx = canvas.getContext("2d");
ctx.drawImage(backgroundImage, 0, 0, 749, 749);
ctx.drawImage(outlineImage, 0, 0, 749, 749);
redraw();
} else if ($("#canvas").width()==750){
$("#canvas").width(1000);
$("#canvas").height(1000);
var ctx = canvas.getContext("2d");
ctx.drawImage(backgroundImage, 0, 0, 999, 999);
ctx.drawImage(outlineImage, 0, 0, 999, 999);
redraw();
}
});
$('#zoomout').click(function () {
if ($("#canvas").width() == 1000) {
$("#canvas").width(750);
$("#canvas").height(750);
var ctx = canvas.getContext("2d");
ctx.drawImage(backgroundImage, 0, 0, 749, 749);
ctx.drawImage(outlineImage, 0, 0, 749, 749);
redraw();
} else if ($("#canvas").width() == 750) {
$("#canvas").width(500);
$("#canvas").height(500);
var ctx = canvas.getContext("2d");
ctx.drawImage(backgroundImage, 0, 0, 499, 499);
ctx.drawImage(outlineImage, 0, 0, 499, 499);
redraw();
}
});
</script>
<div class="colorpick">
<div class="pick" style="background-color:rgb(150, 0, 0);" onclick="hello(this.style.backgroundColor);"></div>
<div class="pick" style="background-color:rgb(0, 0, 152);" onclick="hello(this.style.backgroundColor);"></div>
<div class="pick" style="background-color:rgb(0, 151, 0);" onclick="hello(this.style.backgroundColor);"></div>
<div class="pick" style="background-color:rgb(255, 0, 5);" onclick="hello(this.style.backgroundColor);"></div>
<div class="pick" style="background-color:rgb(255, 255, 0);" onclick="hello(this.style.backgroundColor);"></div>
<div class="pick" style="background-color:rgb(0, 255, 255);" onclick="hello(this.style.backgroundColor);"></div>
<div class="pick" style="background-color:rgb(255, 0, 255);" onclick="hello(this.style.backgroundColor);"></div>
<div class="pick" style="background-color:rgb(255, 150, 0);" onclick="hello(this.style.backgroundColor);"></div>
<div class="pick" style="background-color:rgb(255, 0, 150);" onclick="hello(this.style.backgroundColor);"></div>
<div class="pick" style="background-color:rgb(0, 255, 150);" onclick="hello(this.style.backgroundColor);"></div>
<div class="pick" style="background-color:rgb(150, 0, 255);" onclick="hello(this.style.backgroundColor);"></div>
<div class="pick" style="background-color:rgb(0, 150, 255);" onclick="hello(this.style.backgroundColor);"></div>
</div>
<script>
function hello(e) {
var rgb = e.replace(/^(rgb|rgba)\(/, '').replace(/\)$/, '').replace(/\s/g, '').split(',');
myColor.r = parseInt(rgb[0]);
myColor.g = parseInt(rgb[1]);
myColor.b = parseInt(rgb[2]);
curColor = myColor;
}
</script>
</body>
</html>
You could continuously redraw the canvas content as you're resizing, but there is a more efficient way to do it.
Alternatively...
Continuously redrawing the canvas content is resource intensive. A more efficient way to handle the zoom-animation is to use CSS to do it. When the animation completes, only then resize the canvas and redraw the content at its final zoomed scale. It's true that while animating the transition the canvas content may temporarily appear stretched or squished, but it all happens so quickly that it will not be noticeable.
Save the unzoomed canvas to another in-memory canvas: var memCanvas = canvas.cloneNode() & drawImage the visible canvas to the memCanvas.
Use CSS animations to resize the canvas element to a zoom size. Here's an example on a previous SO Q&A. The animation completes so quickly it probably won't be noticeable if the content resizes disproportionally.
When the animation is complete (the canvas is at its full-zoomed size) you can scale the saved drawing to the new size with drawImage( savedCanvas, 0,0, originalWidth*zoomFactor, originalHeight*zoomFactor) and reset the CSS width and height to the original size.
I'm trying to set up a background in canvas and have some small circles just flow throughout the background, eventually I'll change the shapes and add more details in, but I'm just having trouble with the set up.
I know my code is janky, but are there any suggestions to the structure of the code?
var dx = 1;
var dy = 2;
var circle=new Circle(400,30,10);
var timer;
function Circle(x,y,r){
this.x=x;
this.y=y;
this.r=r;
}
function init() {
// Get the canvas element.
canvas = document.getElementById("canvas");
if (canvas.getContext) {
ctx = canvas.getContext("2d");
ctx.fillStyle = "black";
}
timer=setInterval(draw, 10);
return timer;
}
function gradient (){
var my_gradient=ctx.createLinearGradient(0,0,1000,0);
my_gradient.addColorStop(0,"black");
my_gradient.addColorStop(1,"white");
ctx.fillStyle=my_gradient;
ctx.fillRect(0,0,1000,1000);
ctx.rect(0, 0, 1000, 1000);
stars();
}
function stars(){
for (i = 0; i <= 50; i++) {
// Get random positions for stars
var x = Math.floor(Math.random() * 1000)
var y = Math.floor(Math.random() * 1000)
ctx.fillStyle = "yellow";
//if (x < 30 || y < 30) ctx.fillStyle = "black";
ctx.beginPath();
ctx.arc(x, y, 3, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fill();
}
}
function move(){
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = gradient.my_gradient;
ctx.fillRect(0,0,canvas.width,canvas.height);
ctx.fillStyle = "#003300";
drawBall(circle);
if (circle.x +dx > canvas.width || circle.x +dx < 0)
dx=-dx;
if(circle.y+dy>bar.y && circle.x>bar.x && circle.x<bar.x+barImg.width)
dy=-dy;
if (circle.y +dy > canvas.height || circle.y +dy < 0)
dy=-dy;
circle.x += dx;
circle.y += dy;
}
I tried to code a working exemple. Here stars are popping up continuously.
HTML
<!DOCTYPE html>
<html>
<head>
<title>Exemple</title>
</head>
<body>
<canvas id="viewport"></canvas>
<script src='test.js'></script>
</body>
</html>
JS
var doc = document;
var canvas = doc.getElementById('viewport');
var ctx = canvas.getContext('2d');
var settings = {
area : {
height : 100,
width : 100
}
};
canvas.width = settings.area.width;
canvas.height = settings.area.height;
function draw() {
for (var i = 10; i--;) {
var x = Math.floor(Math.random() * 1000)
var y = Math.floor(Math.random() * 1000)
ctx.beginPath();
ctx.arc(x, y, 3, 0, Math.PI * 2, true);
ctx.fillStyle = "yellow";
ctx.closePath();
ctx.fill();
}
}
function gameLoop (render, element) {
var running, lastFrame = +new Date;
function loop( now ) {
// stop the loop if render returned false
if ( running !== false ) {
requestAnimationFrame( loop, element );
running = render( now - lastFrame );
lastFrame = now;
}
}
loop( lastFrame );
}
gameLoop (function (deltaT) {
draw();
}, canvas );
Here is the fiddle : https://jsfiddle.net/q4q0uLht/
----- EDIT -----
/*
Basic config
*/
var doc = document,
canvas = doc.getElementById('viewport'),
ctx = canvas.getContext('2d');
var settings = {
canvas: {
height: 200,
width: 300
}
}
canvas.height = settings.canvas.height;
canvas.width = settings.canvas.width;
canvas.style.border = '1px #000 solid';
/*
easy gets a random number, inside a range of [0, x);
*/
function getRandomNumber(x) {
return parseInt(Math.random() * x, 10);
}
/*
Checks if the obj passed in argument is still in the canvas limits
*/
function incorrectPosition(obj) {
return obj.x < 0 || obj.y < 0 || obj.x > settings.canvas.width || obj.y > settings.canvas.height;
}
/*
stars array and Star object.
*/
var stars = [];
function Star(r) {
this.x = getRandomNumber(canvas.width);
this.y = getRandomNumber(canvas.height);
this.r = r || 10;
this.move = function(dx, dy) {
this.x += dx;
this.y += dy;
};
}
/*
This function adds new stars,
calculates new coordinates of each star,
and removes them from the stars array
when they are out of the canvas limits.
*/
function update() {
var len = stars.length;
if (len < 10) {
stars.push(new Star());
}
for (var i = len; i--;) {
var star = stars[i];
star.move(1, 2);
if (incorrectPosition(star)) {
stars.splice(i, 1);
}
}
}
/*
This function clears the canvas each turn and
draws each star which is stored inside the stores array.
*/
function draw() {
ctx.clearRect(0, 0, settings.canvas.width, settings.canvas.height);
var len = stars.length;
for (var i = len; i--;) {
var star = stars[i];
ctx.beginPath();
ctx.arc(star.x, star.y, 3, 0, Math.PI * 2, true);
ctx.fillStyle = "yellow";
ctx.closePath();
ctx.fill();
}
}
// Here is the loop inside which are called functions
setInterval(loop, 33);
function loop() {
update(); // update first
draw(); // then draw
}
<!DOCTYPE html>
<html>
<head>
<title>Exemple</title>
</head>
<body>
<canvas id="viewport"></canvas>
<script src='test.js'></script>
</body>
</html>
I made this (run snippet below)
var Canvas = document.getElementById('c');
var ctx = Canvas.getContext('2d');
var resize = function() {
Canvas.width = Canvas.clientWidth;
Canvas.height = Canvas.clientHeight;
};
window.addEventListener('resize', resize);
resize();
var elements = [];
var presets = {};
presets.shard = function (x, y, s, random, color) {
return {
x: x,
y: y,
draw: function(ctx, t) {
this.x += 0;
this.y += 0;
var posX = this.x + + Math.sin((50 + x + (t / 10)) / 100) * 5;
var posy = this.y + + Math.sin((55 + x + (t / 10)) / 100) * 7;
ctx.beginPath();
ctx.fillStyle = color;
ctx.moveTo(posX, posy);
ctx.lineTo(posX+random,posy+random);
ctx.lineTo(posX+random,posy+random);
ctx.lineTo(posX+0,posy+50);
ctx.closePath();
ctx.fill();
}
}
};
for(var x = 0; x < Canvas.width; x++) {
for(var y = 0; y < Canvas.height; y++) {
if(Math.round(Math.random() * 60000) == 1) {
var s = ((Math.random() * 5) + 1) / 10;
if(Math.round(Math.random()) == 1){
var random = Math.floor(Math.random() * 100) + 10;
var colorRanges = ['#8c8886', '#9c9995'];
var color = colorRanges[Math.floor(Math.random() * colorRanges.length)];
elements.push(presets.shard(x, y, s, random, color));
}
}
}
}
setInterval(function() {
ctx.clearRect(0, 0, Canvas.width, Canvas.height);
var time = new Date().getTime();
for (var e in elements)
elements[e].draw(ctx, time);
}, 10);
<canvas id="c" width="1000" height="1000"\>
I just need to add one feature to be able to use it on the site I'm building it for. Some of the floating shards need to be blurred to give a sense of depth.
Can Canvas do this, and if so, how?
context.filter = 'blur(10px)';
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/filter
I used this few months ago, maybe it could work for you as well :
var canvas = document.getElementById("heroCanvas");
var canvasContext = canvas.getContext("2d");
var canvasBackground = new Image();
canvasBackground.src = "image.jpg";
var drawBlur = function() {
// Store the width and height of the canvas for below
var w = canvas.width;
var h = canvas.height;
// This draws the image we just loaded to our canvas
canvasContext.drawImage(canvasBackground, 0, 0, w, h);
// This blurs the contents of the entire canvas
stackBlurCanvasRGBA("heroCanvas", 0, 0, w, h, 100);
}
canvasBackground.onload = function() {
drawBlur();
}
Here the source : http://zurb.com/playground/image-blur-texture