Move to Mouse Click in SVG path (d3.js) - javascript

I need to create a circle and move it to the closest point in an SVG path on mousedown event
here is the jsfiddle
The Code:
var points = [[180,300],[234,335],[288,310],[350,290],[405,300],[430,305],[475,310],[513,300],[550,280]];
var width = 1000, height = 600;
var line = d3.svg.line().interpolate("cardinal");
var svg = d3.select("#Con").append("svg").attr("width", width).attr("height", height);
var path = svg.append("path").datum(points).attr("d", line);
var line = svg.append("line");
var circle = svg.append(" circle").attr("cx", -10).attr("cy", -10).attr("r", 3.5);
svg.append("rect").attr("width", width).attr("height", height).on("mousedown", mouseclick);
function mouseclick() {
var m = d3.mouse(this),p = closestPoint(path.node(), m);
circle.attr("cx", p[0]).attr("cy", p[1]);
}
function closestPoint(pathNode, point) {
var pathLength = pathNode.getTotalLength(),precision = 8,best,bestLength,bestDistance = Infinity;
for (var scan, scanLength = 0, scanDistance; scanLength <= pathLength; scanLength += precision) {
if ((scanDistance = distance2(scan = pathNode.getPointAtLength(scanLength))) < bestDistance) {
best = scan, bestLength = scanLength, bestDistance = scanDistance;
}
}
precision /= 2;
while (precision > 0.5) {
var before,after,beforeLength,afterLength,beforeDistance,afterDistance;
if ((beforeLength = bestLength - precision) >= 0 && (beforeDistance = distance2(before = pathNode.getPointAtLength(beforeLength))) < bestDistance) {
best = before, bestLength = beforeLength, bestDistance = beforeDistance;
} else if ((afterLength = bestLength + precision) <= pathLength && (afterDistance = distance2(after = pathNode.getPointAtLength(afterLength))) < bestDistance) {
best = after, bestLength = afterLength, bestDistance = afterDistance;
} else {
precision /= 2;
}
}
best = [best.x, best.y];
best.distance = Math.sqrt(bestDistance);
return best;
function distance2(p) {
var dx = p.x - point[0],dy = p.y - point[1];
return dx * dx + dy * dy;
}
}
I need to move the circle to the closest point in the path when I click in the SVG space
In my code the circle moves without animation and I need to animate it so that it moves on the path from point to point
I need it to always move with one speed whether it moves a large or small distance
like this:
https://a.top4top.net/p_11885szd41.gif

I'm not using your code but I hope you'll get the idea.
Instead of using a circle I'm using the track:
<use id="theUse" xlink:href="#track"
This track has a stroke-dasharray of ".1 398.80" This means a dash of .1 (very very small) and a gap of 398.80 as long as the track. The stroke-width is 7 with stroke-linecap= "round"and this is transforming the dash into a circle. I'm changing the position of the dash (the circle) using stroke-dashoffset and in order to animate the change I'm using transition: stroke-dashoffset 1s;in the css.
I hope it helps.
let m;
let L = track.getTotalLength();
let _start = {x:180,y:30}
let _end = {x:550,y:280}
let l = dist(_start, _end);
theUse.setAttributeNS(null,"stroke-dashoffset",L);
svg.addEventListener("click",(e)=>{
m = oMousePosSVG(e)
let pos = m.x - _start.x;
let theDistance = map(pos,_start.x,_end.x,0,L)
let s_dof = constrain(L-theDistance, .5, L-.5)
theUse.setAttributeNS(null,"stroke-dashoffset",s_dof)
})
function oMousePosSVG(e) {
var p = svg.createSVGPoint();
p.x = e.clientX;
p.y = e.clientY;
var ctm = svg.getScreenCTM().inverse();
var p = p.matrixTransform(ctm);
return p;
}
function dist(p1, p2) {
let dx = p2.x - p1.x;
let dy = p2.y - p1.y;
return Math.sqrt(dx * dx + dy * dy);
}
function map(n, a, b, _a, _b) {
let d = b - a;
let _d = _b - _a;
let u = _d / d;
return _a + n * u;
}
function constrain(n, low, high) {
return Math.max(Math.min(n, high), low);
};
svg {
border: 1px solid;
}
path {
fill: none;
}
#theUse {
transition: stroke-dashoffset 1s;
}
<svg id="svg" viewBox="150 250 450 100">
<defs>
<path id="track" d="M180,300Q223.2,334,234,335C250.2,336.5,270.6,316.75,288,310S332.45,291.5,350,290S393,297.75,405,300S419.5,303.5,430,305S462.55,310.75,475,310S501.75,304.5,513,300Q520.5,297,550,280"></path>
</defs>
<use xlink:href="#track" stroke="black" />
<use id="theUse" xlink:href="#track" stroke-width="7" stroke-dasharray = ".1 398.80" stroke="red" stroke-linecap= "round" />
</svg>

This answer modifies your code to move a circle from the start of the line or from the last mouse click to the current mouse click. It animates the movement of the circle by calling an animate method with setTimeout until the circle has moved from the beginning of the line to the point where the mouse was clicked.
The interesting code is here:
// getAnimate returns a function that is within a closure
function getAnimate(pLength, path, currentIndex, finishPos, forward){
let animate = function (){
let scan = path.node().getPointAtLength(currentIndex);
if (scan.x < finishPos || !forward && scan.x > finishPos){
circle.attr("cx", scan.x).attr("cy", scan.y);
}
if (forward){
currentIndex += 1;
lastIndex = currentIndex;
if (scan.x < finishPos){
setTimeout(animate, 50);
}
} else {
currentIndex -= 1;
lastIndex = currentIndex;
if (scan.x > finishPos){
setTimeout(animate, 50);
}
}
}
return animate;
}
var points = [[80,100],[134,115],[188,130],[250,120],[305,120],[330,101],[375,103],[413,100],[550,90]];
var width = 500, height = 200;
var line = d3.svg.line().interpolate("cardinal");
var svg = d3.select("#Con").append("svg").attr("width", width).attr("height", height);
var path = svg.append("path").datum(points).attr("d", line);
var line = svg.append("line");
var circle = svg.append("circle").attr("cx", -10).attr("cy", -10).attr("r", 3.5);
svg.append("rect").attr("width", width).attr("height", height).on("mousedown", mouseclick);
var lastIndex = 0;
function mouseclick() {
let m = d3.mouse(this);
let p = closestPoint(path.node(), m);
let forward = true;
let currentPoint = path.node().getPointAtLength(lastIndex);
if (p[0] < currentPoint.x){
forward = false;
}
let pathLength = path.node().getTotalLength();
getAnimate(pathLength, path, lastIndex, p[0], forward)();
}
function getAnimate(pLength, path, currentIndex, finishPos, forward){
let animate = function (){
let scan = path.node().getPointAtLength(currentIndex);
if (scan.x < finishPos || !forward && scan.x > finishPos){
circle.attr("cx", scan.x).attr("cy", scan.y);
}
if (forward){
currentIndex += 1;
lastIndex = currentIndex;
if (scan.x < finishPos){
setTimeout(animate, 50);
}
} else {
currentIndex -= 1;
lastIndex = currentIndex;
if (scan.x > finishPos){
setTimeout(animate, 50);
}
}
}
return animate;
}
function closestPoint(pathNode, point) {
var pathLength = pathNode.getTotalLength(),precision = 8,best,bestLength,bestDistance = Infinity;
for (var scan, scanLength = 0, scanDistance; scanLength <= pathLength; scanLength += precision) {
if ((scanDistance = distance2(scan = pathNode.getPointAtLength(scanLength))) < bestDistance) {
best = scan, bestLength = scanLength, bestDistance = scanDistance;
}
}
precision /= 2;
while (precision > 0.5) {
var before,after,beforeLength,afterLength,beforeDistance,afterDistance;
if ((beforeLength = bestLength - precision) >= 0 && (beforeDistance = distance2(before = pathNode.getPointAtLength(beforeLength))) < bestDistance) {
best = before, bestLength = beforeLength, bestDistance = beforeDistance;
} else if ((afterLength = bestLength + precision) <= pathLength && (afterDistance = distance2(after = pathNode.getPointAtLength(afterLength))) < bestDistance) {
best = after, bestLength = afterLength, bestDistance = afterDistance;
} else {
precision /= 2;
}
}
best = [best.x, best.y];
best.distance = Math.sqrt(bestDistance);
return best;
function distance2(p) {
var dx = p.x - point[0],dy = p.y - point[1];
return dx * dx + dy * dy;
}
}
* {
margin: 0;
padding: 0;
}
#Con {
border: 1px solid black;
margin: auto;
width: 1000px;
height: 600px;
}
#Map{
z-index: -1000;
position: absolute;
}
#Cha {
position: absolute;
}
path {
z-index: 1000;
fill: none;
stroke: #000;
stroke-width: 1.5px;
}
line {
fill: none;
stroke: red;
stroke-width: 1.5px;
}
circle {
fill: red;
}
rect {
fill: none;
pointer-events: all;
}
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.js?2.5.0"></script>
<div id="Con"></div>

Related

HTML5 Canvas | Bouncing Balls | Looping through an image array and placing different background images on each ball, centered

I am having a bit of a nightmare applying different background images onto balls that bounce around in the canvas under the effect of gravity and the bounds of my canvas, eventually settling down and stacking at the bottom.
I have created an image array, that I am trying to loop through and create a ball for each image, where the image becomes a centred background.
I can centre the image, but this makes the balls appear to leave the bounds of my canvas. So have reverted that change.
I am unable to get the background image to be different from the previous ball. Where the image shown on all balls is the last image in the array. However the number of balls created does reflect the number of images in the array.
Here is a link to a codepen:
https://codepen.io/jason-is-my-name/pen/BbNRXB
html, body{
width:100%;
height:100%;
margin: 0;
padding: 0;
background: #333333;
}
*{
margin: 0;
padding: 0;
}
.container {
width: 410px;
height: 540px;
}
#ball-stage{
width: 100%;
height: 100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
<canvas id="ball-stage" ></canvas>
</div>
<script>
/*
/*
* Created by frontside.com.au
* Amended by Jason
*/
$(document).ready(function () {
$.ajaxSetup({
cache: true
});
var url1 = "https://code.createjs.com/easeljs-0.6.0.min.js";
$.getScript(url1, function () {
new App();
})
});
function App() {
var self = this;
self.running = false;
self.initialized = false;
var stageClicked = false;
var stage, canvas;
var canvasWidth = 410;
var canvasHeight = 540;
var bounce = -0.75;
var balls = [];
var _gravityY = 1;
var _gravityX = 0;
var FPS = 30;
var infoText, detailsText;
var ballsInitalized = false;
var iOS = navigator.userAgent.match(/(iPod|iPhone|iPad)/);
self.initialize = function () {
toggleListeners(true);
self.initCanvas();
self.initGame();
};
var toggleListeners = function (enable) {
if (!enable) return;
};
self.refresh = function () {}
self.initCanvas = function () {
canvas = $("#ball-stage").get(0);
stage = new createjs.Stage(canvas);
window.addEventListener('resize', onStageResize, false);
onStageResize();
createjs.Touch.enable(stage);
createjs.Ticker.addListener(tick);
createjs.Ticker.setFPS(FPS);
self.initialized = true;
}
self.initGame = function () {
initBalls(canvasWidth, canvasHeight);
}
var onStageResize = function () {
stage.canvas.width = canvasWidth;
stage.canvas.height = canvasHeight;
}
var initBalls = function (stageX, stageY) {
var imagesArray = ["img-1.png","img-2.png","img-3.png","img-4.png","img-5.png","img-6.png","img-7.png","img-8.png"];
for (var i = 0; i < imagesArray.length; i++) {
console.log(i);
var imageArray = imagesArray[i];
console.log(imageArray);
setTimeout(function () {
var arrayImage = new Image();
console.log(arrayImage);
arrayImage.onload = function(){
addBall(arrayImage, stageX / 2, 0);
}
arrayImage.src = imageArray;
}, i * 1000);
}
}
var addBall = function (img, x, y) {
console.log(img);
var shape = new createjs.Shape();
shape.id = balls.length;
shape.radius = 51.25;
shape.mass = shape.radius;
shape.x = x;
shape.y = y;
shape.vx = rand(-3, 3);
shape.vy = rand(-3, 3);
var image = new Image();
image.src = img;
shape.graphics.beginBitmapFill(img,'repeat').drawCircle(0, 0, shape.radius);
stage.addChild(shape);
balls.push(shape);
}
var numBalls = function () {
return balls.length;
}
var tick = function () {
balls.forEach(move);
for (var ballA, i = 0, len = numBalls() - 1; i < len; i++) {
ballA = balls[i];
for (var ballB, j = i + 1; j < numBalls(); j++) {
ballB = balls[j];
checkCollision(ballA, ballB);
}
}
stage.update();
}
var rotate = function (x, y, sin, cos, reverse) {
return {
x: (reverse) ? (x * cos + y * sin) : (x * cos - y * sin),
y: (reverse) ? (y * cos - x * sin) : (y * cos + x * sin)
};
}
var checkCollision = function (ball0, ball1) {
var dx = ball1.x - ball0.x,
dy = ball1.y - ball0.y,
dist = Math.sqrt(dx * dx + dy * dy);
//collision handling code here
if (dist < ball0.radius + ball1.radius) {
//calculate angle, sine, and cosine
var angle = Math.atan2(dy, dx),
sin = Math.sin(angle),
cos = Math.cos(angle),
//rotate ball0's position
pos0 = {
x: 0,
y: 0
}, //point
//rotate ball1's position
pos1 = rotate(dx, dy, sin, cos, true),
//rotate ball0's velocity
vel0 = rotate(ball0.vx, ball0.vy, sin, cos, true),
//rotate ball1's velocity
vel1 = rotate(ball1.vx, ball1.vy, sin, cos, true),
//collision reaction
vxTotal = vel0.x - vel1.x;
vel0.x = ((ball0.mass - ball1.mass) * vel0.x + 2 * ball1.mass * vel1.x) /
(ball0.mass + ball1.mass);
vel1.x = vxTotal + vel0.x;
//update position - to avoid objects becoming stuck together
var absV = Math.abs(vel0.x) + Math.abs(vel1.x),
overlap = (ball0.radius + ball1.radius) - Math.abs(pos0.x - pos1.x);
pos0.x += vel0.x / absV * overlap;
pos1.x += vel1.x / absV * overlap;
//rotate positions back
var pos0F = rotate(pos0.x, pos0.y, sin, cos, false),
pos1F = rotate(pos1.x, pos1.y, sin, cos, false);
//adjust positions to actual screen positions
// ball1.x = ball0.x + pos1F.x;
setBallX(ball1, ball0.x + pos1F.x)
//ball1.y = ball0.y + pos1F.y;
setBallY(ball1, ball0.y + pos1F.y)
// ball0.x = ball0.x + pos0F.x;
setBallX(ball0, ball0.x + pos0F.x)
// ball0.y = ball0.y + pos0F.y;
setBallY(ball0, ball0.y + pos0F.y)
//rotate velocities back
var vel0F = rotate(vel0.x, vel0.y, sin, cos, false),
vel1F = rotate(vel1.x, vel1.y, sin, cos, false);
ball0.vx = vel0F.x;
ball0.vy = vel0F.y;
ball1.vx = vel1F.x;
ball1.vy = vel1F.y;
}
}
var checkWalls = function (ball) {
if (ball.x + ball.radius > canvas.width) {
// ball.x = canvas.width - ball.radius;
setBallX(ball, canvas.width - ball.radius)
ball.vx *= bounce;
} else
if (ball.x - ball.radius < 0) {
// ball.x = ball.radius;
setBallX(ball, ball.radius)
ball.vx *= bounce;
}
if (ball.y + ball.radius > canvas.height) {
// ball.y = canvas.height - ball.radius;
setBallY(ball, canvas.height - ball.radius)
ball.vy *= bounce;
} else
if (ball.y - ball.radius < 0) {
//ball.y = ball.radius;
setBallY(ball, ball.radius)
ball.vy *= bounce;
}
}
var move = function (ball) {
ball.vy += _gravityY;
ball.vx += _gravityX;
setBallX(ball, ball.x + ball.vx)
setBallY(ball, ball.y + ball.vy)
checkWalls(ball);
}
var setBallX = function (ball, x) {
if (isNaN(ball.pointerID)) {
ball.x = x
}
}
var setBallY = function (ball, y) {
if (isNaN(ball.pointerID)) {
ball.y = y
}
}
var rand = function (min, max) {
return Math.random() * (max - min) + min;
return (Math.random() * max) + min;
}
self.initialize();
return self;
}
window.log = function f() {
log.history = log.history || [];
log.history.push(arguments);
if (this.console) {
var args = arguments,
newarr;
args.callee = args.callee.caller;
newarr = [].slice.call(args);
if (typeof console.log === 'object') log.apply.call(console.log, console, newarr);
else console.log.apply(console, newarr);
}
};
(function (a) {
function b() {}
for (var c = "assert,count,debug,dir,dirxml,error,exception,group,groupCollapsed,groupEnd,info,log,markTimeline,profile,profileEnd,time,timeEnd,trace,warn".split(","), d; !!(d = c.pop());) {
a[d] = a[d] || b;
}
})
(function () {
try {
console.log();
return window.console;
} catch (a) {
return (window.console = {});
}
}());
</script>
I have been trapped at this point in the code for about a week now and could really do with some genius' help!
Aims:
Add balls equivalent to the length of the image array.
Each ball with have its respective image as a centred background.
The balls will not leave the bounds of the canvas.
Relevant code:
initBalls()
addBall()
Thanks, Jason.
https://codepen.io/prtjohanson/pen/vPKQBg
What needed to be changed:
for (let i = 0; i < imagesArray.length; i++) {
console.log(i);
const imageArray = imagesArray[i];
setTimeout(function() {
var arrayImage = new Image();
arrayImage.onload = function() {
addBall(arrayImage, stageX / 2, 0);
};
arrayImage.src = imageArray;
}, i * 1000);
}
By the time the setTimeout callback fired, your for loop had already finished and with a var declaration, the for loop iteration has no scope of its own, with a let, each iteration has its own scope like a function does.
If it must run on browsers that don't have let or const keywords, let me know, I can provide a solution for them as well
This will work in IE11 and other browsers that don't support ES6
for (var i = 0; i < imagesArray.length; i++) {
(function(imageArray) {
setTimeout(function() {
var arrayImage = new Image();
arrayImage.onload = function() {
console.log('Add'+i);
addBall(arrayImage, stageX / 2, 0);
};
arrayImage.src = imageArray;
}, i * 1000);
})(imagesArray[i]);
}
To get the images centered, without them going out of the bounds of canvas, use a 2D transform on the beginBitmapFill operation:
var transform = new createjs.Matrix2D();
transform.appendTransform(-shape.radius, -shape.radius, 1, 1, 0);
shape.graphics.beginBitmapFill(img, "repeat", transform).drawCircle(0, 0, shape.radius);
As for there being not as many balls as there are URLs in the array, it seems that sometimes the image source URL prompts "I am not a robot" captcha. If replaced with URL-s under your control, the issue should go away.

Scrollable board for HTML5 GAME should use SVG's or Canvas

I wish to create a simple webgame that involves a tiled board. I have a collection of svg's for the background of each square (i.e one for grass, one for stone, one for dirt etc). I also have svg's for items that will be displayed on the layer above the background (such as trees, wood, sword).
I have an in memory database of what the background for each square is and if and which item it contains.
I wish to be able to:
* Zoom in or out
* Scroll left or right
* Scolll up or down
* Have items displayed above the background for that square
Only needs to work in recent versions of modern browsers
What is the best approach for this:
1. Have a canvas object. Get the current zoom, top most XY, canvas width and canvas height. Loop though the squares in the in memory database and print the corresponding SVG's in the correct locations. Each time it is scrolled or zoomed reprint the entire board.
2. Have a div. Get the current zoom, top most XY, canvas width and canvas height. Loop though the squares in the in memory database and create SVG's in the correct locations.
Each time the board scrolls add new SVGs as they become visible, delete SVGs as they move of the board. Translate all the existing SVGs by the appropriate amount.
Each time the board zooms enlarge or shrink all the existing SVGs based on the new zoom level.
3. Some third approach that I am unaware of.
The example below uses two modules svg to load images (Any image format will work) & board which handles panning, zooming & rendering. It also provides an onClick event which will give you an object that describes the tile that has been clicked on.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
background-color: black;
}
canvas {
display: block;
margin: 30px auto 0px auto;
border: solid 1px white;
border-radius: 10px;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script type="application/javascript">
var svg = function() {
"use strict";
var svgImages = [];
var areImagesLoaded = true;
var isPageLoaded = false;
addEventListener("load",function() { isPageLoaded = true; isFinished(); });
var exports = {
onload: function() {},
request: null,
get: null
};
function isFinished() {
if (isPageLoaded && areImagesLoaded) {
exports.onload();
}
}
function onSvgError() {
this.isDone = true;
console.warn("SVG " + this.src + " failed to load.");
}
function onSvgLoaded() {
this.isDone = true;
for (var id in svgImages) {
if (!svgImages[id].isDone) {
return;
}
}
areImagesLoaded = true;
isFinished();
}
function request(id,path) {
if (svgImages[id]) {
return;
}
areImagesLoaded = false;
var img = document.createElement("img");
img.onerror = onSvgError;
img.onload = onSvgLoaded;
img.isDone = false;
img.id = id;
img.src = path;
svgImages[id] = img;
}
function get(id) {
return svgImages[id];
}
exports.request = request;
exports.get = get;
return exports;
}();
var board = function() {
"use strict";
var canvasWidth = 0;
var canvasHeight = 0;
var canvas = null;
var ctx = null;
var frameRequested = false;
var tileWidth = 0;
var tileHeight = 0;
var tileTypes = [];
var boardWidth = 0;
var boardHeight = 0;
var board = [];
var hasInitialized = false;
var camera = {
x: 0.0,
y: 0.0,
zoom: 1.0
};
function mapToBoard(x,y) {
var invZoom = 1.0 / camera.zoom;
return [
(x - (canvasWidth >> 1)) * invZoom - camera.x,
(y - (canvasHeight >> 1)) * invZoom - camera.y
];
}
var isMouseDragging = false;
var mouseStartX = 0;
var mouseStartY = 0;
var mouseLastX = 0;
var mouseLastY = 0;
var tileEvent = {
background: "",
foreground: "",
x: 0,
y: 0
};
function onTileSelected(e) {
}
function onMouseDown(e) {
isMouseDragging = true;
var bounds = canvas.getBoundingClientRect();
mouseStartX = mouseLastX = e.clientX - bounds.left;
mouseStartY = mouseLastY = e.clientY - bounds.top;
}
function onMouseUp(e) {
isMouseDragging = false;
var bounds = canvas.getBoundingClientRect()
var x = e.clientX - bounds.left - mouseStartX;
var y = e.clientY - bounds.top - mouseStartY;
var l = Math.sqrt(x * x + y * y);
if (l < 2.0) {
[x,y] = mapToBoard(e.clientX - bounds.left,e.clientY - bounds.top);
if (x > 0 && y > 0 && x < boardWidth * tileWidth && y < boardHeight * tileHeight) {
x = (x / tileWidth) | 0;
y = (y / tileHeight) | 0;
var tile = board[x + y * boardWidth];
tileEvent.background = tile.background;
tileEvent.foreground = tile.foreground;
tileEvent.x = x;
tileEvent.y = y;
} else {
tileEvent.background = "";
tileEvent.foreground = "";
tileEvent.x = -1;
tileEvent.y = -1;
}
onTileSelected(tileEvent);
}
}
function onMouseMove(e) {
if (hasInitialized && isMouseDragging) {
var bounds = canvas.getBoundingClientRect();
var x = e.clientX - bounds.left;
var y = e.clientY - bounds.top;
camera.x += (x - mouseLastX) / camera.zoom;
camera.y += (y - mouseLastY) / camera.zoom;
mouseLastX = x;
mouseLastY = y;
requestDraw();
}
}
function onWheel(e) {
if (Math.sign(e.deltaY) === -1) {
camera.zoom *= 1.1;
} else {
camera.zoom *= 0.9;
}
requestDraw();
}
function draw() {
ctx.fillStyle = "gray";
ctx.fillRect(-canvasWidth >> 1,-canvasHeight >> 1,canvasWidth,canvasHeight);
var _tileWidth = tileWidth * camera.zoom;
var _tileHeight = tileHeight * camera.zoom;
var _ox = camera.x * camera.zoom;
var _oy = camera.y * camera.zoom;
var _x = _ox;
var _y = _oy;
for (var x = 0; x <boardWidth; ++x) {
for (var y = 0; y < boardHeight; ++y) {
var index = x + y * boardWidth;
var tile = board[index];
var background = tileTypes[tile.background];
var foreground = tileTypes[tile.foreground];
if (background) {
ctx.drawImage(
background,
_x,
_y,
_tileWidth,
_tileHeight
);
}
if (foreground) {
ctx.drawImage(
foreground,
_x,
_y,
_tileWidth,
_tileHeight
);
}
_y += _tileHeight;
}
_y = _oy;
_x += _tileWidth;
}
frameRequested = false;
}
function requestDraw() {
if (!frameRequested) {
frameRequested = true;
requestAnimationFrame(draw);
}
}
return {
BACKGROUND: 0,
FOREGROUND: 1,
set canvas(canvasID) {
if (!hasInitialized) {
canvas = document.getElementById(canvasID);
canvas.onmousedown = onMouseDown;
canvas.onmouseup = onMouseUp;
canvas.onmousemove = onMouseMove;
canvas.onwheel = onWheel;
ctx = canvas.getContext("2d");
}
},
set canvasWidth(w) {
if (!hasInitialized && canvas) {
canvasWidth = canvas.width = w;
}
},
set canvasHeight(h) {
if (!hasInitialized && canvas) {
canvasHeight = canvas.height = h;
}
},
set tileWidth(w) {
if (!hasInitialized) {
tileWidth = w;
}
},
set tileHeight(h) {
if (!hasInitialized) {
tileHeight = h;
}
},
set width(w) {
if (!hasInitialized) {
boardWidth = w;
}
},
set height(h) {
if (!hasInitialized) {
boardHeight = h;
}
},
set onTileSelected(callback) {
onTileSelected = callback;
},
get width() {
return boardWidth;
},
get height() {
return boardHeight;
},
get onTileSelected() {
return onTileSelected;
},
defineTileTypes: function(types) {
if (types.length % 2 !== 0) {
return;
}
for (var i = 0; i < types.length; i += 2) {
var id = types[i];
var img = types[i + 1];
tileTypes[id] = img;
}
},
init: function() {
camera.x = -(boardWidth >> 1) * tileWidth;
camera.y = -(boardHeight >> 1) * tileHeight;
ctx.translate(canvasWidth >> 1,canvasHeight >> 1);
board.length = boardWidth * boardHeight;
for (var i = 0; i < board.length; ++i) {
board[i] = {
background: "",
foreground: ""
};
}
hasInitialized = true;
requestAnimationFrame(draw);
},
set: function(type,id,x,y) {
if (hasInitialized
&& tileTypes[id]
&& x > -1
&& x < boardWidth
&& y > -1
&& y < boardHeight) {
var index = x + y * boardWidth;
if (type === this.BACKGROUND) {
board[index].background = id;
} else {
board[index].foreground = id;
}
requestDraw();
}
}
};
}();
void function() {
"use strict";
svg.request("grass","https://i.stack.imgur.com/CkvU7.png");
svg.request("water","https://i.stack.imgur.com/an6a5.png");
svg.onload = function() {
board.canvas = "canvas";
board.canvasWidth = 180;
board.canvasHeight = 160;
board.tileWidth = 25;
board.tileHeight = 25;
board.width = 20;
board.height = 20;
board.defineTileTypes([
"GRASS",svg.get("grass"),
"WATER",svg.get("water")
]);
board.init();
for (var x = 0; x < board.width; ++x) {
for (var y = 0; y < board.height; ++y) {
board.set(board.BACKGROUND,"WATER",x,y);
if (Math.random() > 0.2) {
board.set(board.BACKGROUND,"GRASS",x,y);
} else {
board.set(board.BACKGROUND,"WATER",x,y);
}
}
}
}
board.onTileSelected = function(e) {
if (e.background === "GRASS") {
board.set(board.BACKGROUND,"WATER",e.x,e.y);
} else {
board.set(board.BACKGROUND,"GRASS",e.x,e.y);
}
}
}();
</script>
</body>
</html>

Snake game: how to check if the head collides with its own body

You may have played Snake, a game where you have to eat food to grow and you fail if you collide with the snake's body or certain obstacles. The first part was easy, but the latter seems impossible to achieve.
I have tried to make a for loop check if the last element of my snake array is colliding with its other parts. My condition was like this: if the x position of the last item in my array is bigger than any of the array items' x position, and smaller than their x position plus their width, and so on. That didn't work.
Here's my code :
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<canvas id="myCanvas" width="200px" height="200px" style="border:1px solid black"/>
<script>
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var yPos = 20;
var width = 15;
var variable = 1;
var currentDir = 1;
//var xPos = (width+5)*variable;
var xPos = 20;
var myArr = [{myX:xPos,myY:yPos},{myX:xPos,myY:yPos},{myX:xPos,myY:yPos}];
var downPressed = false;
var upPressed = false;
var leftPressed = false;
var rightPressed = false;
var first = [0,20,40,60,80,100,120,140,160,180];
var firstX = Math.floor(Math.random()*10);
var firstY = Math.floor(Math.random()*10);
var okayed = first[firstX];
var notOkayed = first[firstY];
var maths = myArr[myArr.length-1];
function drawFood() {
ctx.beginPath();
ctx.rect(okayed,notOkayed,15,15);
ctx.fillStyle = "red";
ctx.fill();
ctx.closePath();
}
function drawRectangle() {
ctx.clearRect(0,0,200,200);
drawFood();
for(var i = 0;i<myArr.length;i++) {
ctx.beginPath();
ctx.rect(myArr[i].myX,myArr[i].myY,width,15);
ctx.fillStyle = "blue";
ctx.fill();
ctx.closePath();
}
requestAnimationFrame(drawRectangle);
}
setInterval("calledin()",100);
function calledin() {
var secondX = Math.floor(Math.random()*10);
var secondY = Math.floor(Math.random()*10);
var newobj = {myX:myArr[myArr.length-1].myX+20,myY:myArr[myArr.length-1].myY};
var newobjTwo = {myX:myArr[myArr.length-1].myX,myY:myArr[myArr.length-1].myY+20};
var newobjLeft = {myX:myArr[myArr.length-1].myX-20,myY:myArr[myArr.length-1].myY};
var newobjUp = {myX:myArr[myArr.length-1].myX,myY:myArr[myArr.length-1].myY-20};
var okayNewObj = {myX:myArr[1].myX - 20,myY:myArr[1].myY};
if(myArr[myArr.length-1].myX > 180 || myArr[myArr.length-1].myX < 0 || myArr[myArr.length-1].myY > 180 || myArr[myArr.length-1].myY < 0)
{alert("Game Over");window.location.reload();}
if(myArr[myArr.length-1].myX > okayed-5 && myArr[myArr.length-1].myX < okayed+20 && myArr[myArr.length-1].myY < notOkayed+20 &&
myArr[myArr.length-1].myY > notOkayed-5) {
okayed = first[secondX];
notOkayed = first[secondY];
myArr.unshift(okayNewObj);
}
if(currentDir == 1) {
myArr.push(newobj);
myArr.shift();}
if(currentDir == 2) {
myArr.push(newobjTwo);
myArr.shift();
}
if(currentDir == 4) {
myArr.push(newobjLeft);
myArr.shift();
}
if(currentDir == 3) {
myArr.push(newobjUp);
myArr.shift();
}
for(var i = 0;i<myArr.length-2;i++) {
if(myArr[myArr.length-1].myX > myArr[i].myX &&
myArr[myArr.length-1].myX < myArr[i].myX + 15 && myArr[myArr.length-1].myY > myArr[i].myY && myArr[myArr.length-1].myY > myArr[i].myY + 15)
{alert("Game over");window.location.reload();}
}
}
function downed(e) {
if(e.keyCode==40) {if(currentDir != 3) {currentDir = 2;}}
if(e.keyCode==38) {if(currentDir != 2) {currentDir = 3;}}
if(e.keyCode==39) {if(currentDir != 4) {currentDir = 1;}}
if(e.keyCode==37) {if(currentDir != 1) {currentDir = 4;}}
}
function upped(e) {
if(e.keyCode == 40) {downPressed = false;}
}
document.addEventListener("keydown",downed,false);
document.addEventListener("keyup",upped,false);
drawRectangle();
</script>
</body>
</html>
Suppose the snake is represented by an array called snake in which the head is at index snake.length - 1. We have to compare the position of the head against the positions of the body segments at indices 0 through snake.length - 2.
The following code sets okay to false if the snake head has collided with a body segment. Otherwise, okay remains true.
var head = snake[snake.length - 1],
x = head.x,
y = head.y,
okay = true;
for (var i = snake.length - 2; i >= 0; --i) {
if (snake[i].x == x && snake[i].y == y) {
okay = false;
break;
}
}
Below is a snippet in which I have modified your code to clarify the game logic and to simplify many of the calculations.
Instead of working directly with canvas coordinates, I represent each position with the column index x and row index y of a virtual grid cell. This lets us calculate the neighboring grid positions by adding 1 or -1 to x or y. When it comes time to paint the canvas, we multiply the virtual coordinates by the cell size.
I have replaced most of your literal values with variables. For example, instead of setting the canvas dimensions to 200 by 200, we can do this:
canvas.width = numCols * cellSize;
canvas.height = numRows * cellSize;
This lets us change numCols and numRows in one place to resize the whole game grid. All the calculations work out because they evaluate variables instead of using literals.
I altered the key-event handling to recognize the key codes for the W-A-S-D keys in addition to the arrow keys. When the game is embedded in a long web page, as it is here, you'll probably want to use the W-A-S-D keys so that the page doesn't scroll up and down while you're playing.
var canvas,
ctx,
currentDir,
startX = 1,
startY = 1,
startSnakeLength = 3,
snake,
cellSize = 18,
cellGap = 1,
foodColor = '#a2302a',
snakeBodyColor = '#2255a2',
snakeHeadColor = '#0f266b',
numRows = 10,
numCols = 10,
canvasWidth = numCols * cellSize,
canvasHeight = numRows * cellSize;
var food = {};
function placeFood() {
// Find a random location that isn't occupied by the snake.
var okay = false;
while (!okay) {
food.x = Math.floor(Math.random() * numCols);
food.y = Math.floor(Math.random() * numRows);
okay = true;
for (var i = 0; i < snake.length; ++i) {
if (snake[i].x == food.x && snake[i].y == food.y) {
okay = false;
break;
}
}
}
}
function paintCell(x, y, color) {
ctx.fillStyle = color;
ctx.fillRect(x * cellSize + cellGap,
y * cellSize + cellGap,
cellSize - cellGap,
cellSize - cellGap);
}
function paintCanvas() {
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
paintCell(food.x, food.y, foodColor);
var head = snake[snake.length - 1];
paintCell(head.x, head.y, snakeHeadColor);
for (var i = snake.length - 2; i >= 0; --i) {
paintCell(snake[i].x, snake[i].y, snakeBodyColor);
}
}
function updateGame() {
var head = snake[snake.length - 1],
x = head.x,
y = head.y;
// Move the snake.
var tail = snake.shift();
switch (currentDir) {
case 'up':
snake.push(head = { x: x, y: y - 1 });
break;
case 'right':
snake.push(head = { x: x + 1, y: y });
break;
case 'down':
snake.push(head = { x: x, y: y + 1 });
break;
case 'left':
snake.push(head = { x: x - 1, y: y });
break;
}
paintCanvas();
x = head.x;
y = head.y;
// Check for wall collision.
if (x < 0 || x >= numCols || y < 0 || y >= numRows) {
stopGame('wall collision');
return;
}
// Check for snake head colliding with snake body.
for (var i = snake.length - 2; i >= 0; --i) {
if (snake[i].x == x && snake[i].y == y) {
stopGame('self-collision');
return;
}
}
// Check for food.
if (x == food.x && y == food.y) {
placeFood();
snake.unshift(tail);
setMessage(snake.length + ' segments');
}
}
var dirToKeyCode = { // Codes for arrow keys and W-A-S-D.
up: [38, 87],
right: [39, 68],
down: [40, 83],
left: [37, 65]
},
keyCodeToDir = {}; // Fill this from dirToKeyCode on page load.
function keyDownHandler(e) {
var keyCode = e.keyCode;
if (keyCode in keyCodeToDir) {
currentDir = keyCodeToDir[keyCode];
}
}
function setMessage(s) {
document.getElementById('messageBox').innerHTML = s;
}
function startGame() {
currentDir = 'right';
snake = new Array(startSnakeLength);
snake[snake.length - 1] = { x: startX, y: startY };
for (var i = snake.length - 2; i >= 0; --i) {
snake[i] = { x: snake[i + 1].x, y: snake[i + 1].y + 1 };
}
placeFood();
paintCanvas();
setMessage('');
gameInterval = setInterval(updateGame, 200);
startGameButton.disabled = true;
}
function stopGame(message) {
setMessage(message + '<br> ended with ' + snake.length + ' segments');
clearInterval(gameInterval);
startGameButton.disabled = false;
}
var gameInterval,
startGameButton;
window.onload = function () {
canvas = document.getElementById('gameCanvas'),
ctx = canvas.getContext('2d');
canvas.width = numCols * cellSize;
canvas.height = numRows * cellSize;
Object.keys(dirToKeyCode).forEach(function (dir) {
dirToKeyCode[dir].forEach(function (keyCode) {
keyCodeToDir[keyCode] = dir;
})
});
document.addEventListener("keydown", keyDownHandler, false);
startGameButton = document.getElementById('startGameButton');
startGameButton.onclick = startGame;
}
body {
font-family: sans-serif;
}
#gameCanvas {
border: 1px solid #000;
float: left;
margin-right: 15px;
}
#startGameButton, #messageBox {
font-size: 16px;
margin-top: 15px;
}
#messageBox {
line-height: 24px;
}
<canvas id="gameCanvas"></canvas>
<button id="startGameButton">Start game</button>
<div id="messageBox"></div>

Draw random coloured circles on canvas

I am trying to create a animation where circles run from right to left. The circles' colours are selected randomly by a function. I have created a fiddle where one circle runs from right to left. Now my function creates a random colour. This function is executed every second and the circle changes its colour every second, instead of a new circle with the random picked colour become created. How can I change it so that it draws a new circle every second on the canvas and doesn't only change the colour of the circle?
This is my function:
function getRandomElement(array) {
if (array.length == 0) {
return undefined;
}
return array[Math.floor(Math.random() * array.length)];
}
var circles = [
'#FFFF00',
'#FF0000',
'#0000FF'
];
function drawcircles() {
ctx.beginPath();
ctx.arc(ballx * 108, canvasHeight / 2, x*5, 0, 2*Math.PI, false);
ctx.fillStyle = getRandomElement(circles);
ctx.fill();
ctx.closePath;
}
Comments on your question:
You are changing the color of the fillStyle to a random color at each frame. This is the reason why it keeps "changing" color. Set it to the color of the circle:
context.fillStyle = circle.color;
make circles with x, y, diameter, bounciness, speed, and color using an array
draw and update them with requestAnimationFrame (mine is a custom function)
My answer:
I made this just last night, where some circles follow the cursor and "bounce" off the edges of the screen. I tested the code and it works.
I might post a link later, but here is all of the code right now...
<!DOCTYPE html>
<html>
<body>
<canvas id="canvas"></canvas>
<script>
var lastTime = 0;
function requestMyAnimationFrame(callback, time)
{
var t = time || 16;
var currTime = new Date().getTime();
var timeToCall = Math.max(0, t - (currTime - lastTime));
var id = window.setTimeout(function(){ callback(currTime + timeToCall); }, timeToCall);
lastTime = currTime + timeToCall;
return id;
}
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
canvas.width = window.innerWidth - 20;
canvas.height = window.innerHeight - 20;
canvas.style.width = canvas.width + "px";
canvas.style.height = canvas.height + "px";
var circles = [];
var mouse =
{
x: 0,
y: 0
}
function getCoordinates(x, y)
{
return "(" + x + ", " + y + ")";
}
function getRatio(n, d)
{
// prevent division by 0
if (d === 0 || n === 0)
{
return 0;
}
else
{
return n/d;
}
}
function Circle(x,y,d,b,s,c)
{
this.x = x;
this.y = y;
this.diameter = Math.round(d);
this.radius = Math.round(d/2);
this.bounciness = b;
this.speed = s;
this.color = c;
this.deltaX = 0;
this.deltaY = 0;
this.drawnPosition = "";
this.fill = function()
{
context.beginPath();
context.arc(this.x+this.radius,this.y+this.radius,this.radius,0,Math.PI*2,false);
context.closePath();
context.fill();
}
this.clear = function()
{
context.fillStyle = "#ffffff";
this.fill();
}
this.draw = function()
{
if (this.drawnPosition !== getCoordinates(this.x, this.y))
{
context.fillStyle = this.color;
// if commented, the circle will be drawn if it is in the same position
//this.drawnPosition = getCoordinates(this.x, this.y);
this.fill();
}
}
this.keepInBounds = function()
{
if (this.x < 0)
{
this.x = 0;
this.deltaX *= -1 * this.bounciness;
}
else if (this.x + this.diameter > canvas.width)
{
this.x = canvas.width - this.diameter;
this.deltaX *= -1 * this.bounciness;
}
if (this.y < 0)
{
this.y = 0;
this.deltaY *= -1 * this.bounciness;
}
else if (this.y+this.diameter > canvas.height)
{
this.y = canvas.height - this.diameter;
this.deltaY *= -1 * this.bounciness;
}
}
this.followMouse = function()
{
// deltaX/deltaY will currently cause the circles to "orbit" around the cursor forever unless it hits a wall
var centerX = Math.round(this.x + this.radius);
var centerY = Math.round(this.y + this.radius);
if (centerX < mouse.x)
{
// circle is to the left of the mouse, so move the circle to the right
this.deltaX += this.speed;
}
else if (centerX > mouse.x)
{
// circle is to the right of the mouse, so move the circle to the left
this.deltaX -= this.speed;
}
else
{
//this.deltaX = 0;
}
if (centerY < mouse.y)
{
// circle is above the mouse, so move the circle downwards
this.deltaY += this.speed;
}
else if (centerY > mouse.y)
{
// circle is under the mouse, so move the circle upwards
this.deltaY -= this.speed;
}
else
{
//this.deltaY = 0;
}
this.x += this.deltaX;
this.y += this.deltaY;
this.x = Math.round(this.x);
this.y = Math.round(this.y);
}
}
function getRandomDecimal(min, max)
{
return Math.random() * (max-min) + min;
}
function getRoundedNum(min, max)
{
return Math.round(getRandomDecimal(min, max));
}
function getRandomColor()
{
// array of three colors
var colors = [];
// go through loop and add three integers between 0 and 255 (min and max color values)
for (var i = 0; i < 3; i++)
{
colors[i] = getRoundedNum(0, 255);
}
// return rgb value (RED, GREEN, BLUE)
return "rgb(" + colors[0] + "," + colors[1] + ", " + colors[2] + ")";
}
function createCircle(i)
{
// diameter of circle
var minDiameter = 25;
var maxDiameter = 50;
// bounciness of circle (changes speed if it hits a wall)
var minBounciness = 0.2;
var maxBounciness = 0.65;
// speed of circle (how fast it moves)
var minSpeed = 0.3;
var maxSpeed = 0.45;
// getRoundedNum returns a random integer and getRandomDecimal returns a random decimal
var x = getRoundedNum(0, canvas.width);
var y = getRoundedNum(0, canvas.height);
var d = getRoundedNum(minDiameter, maxDiameter);
var c = getRandomColor();
var b = getRandomDecimal(minBounciness, maxBounciness);
var s = getRandomDecimal(minSpeed, maxSpeed);
// create the circle with x, y, diameter, bounciness, speed, and color
circles[i] = new Circle(x,y,d,b,s,c);
}
function makeCircles()
{
var maxCircles = getRoundedNum(2, 5);
for (var i = 0; i < maxCircles; i++)
{
createCircle(i);
}
}
function drawCircles()
{
var ii = 0;
for (var i = 0; ii < circles.length; i++)
{
if (circles[i])
{
circles[i].draw();
ii++;
}
}
}
function clearCircles()
{
var ii = 0;
for (var i = 0; ii < circles.length; i++)
{
if (circles[i])
{
circles[i].clear();
ii++;
}
}
}
function updateCircles()
{
var ii = 0;
for (var i = 0; ii < circles.length; i++)
{
if (circles[i])
{
circles[i].keepInBounds();
circles[i].followMouse();
ii++;
}
}
}
function update()
{
requestMyAnimationFrame(update,10);
updateCircles();
}
function draw()
{
requestMyAnimationFrame(draw,1000/60);
context.clearRect(0,0,canvas.width,canvas.height);
drawCircles();
}
function handleError(e)
{
//e.preventDefault();
//console.error(" ERROR ------ " + e.message + " ------ ERROR ");
}
window.addEventListener("load", function()
{
window.addEventListener("error", function(e)
{
handleError(e);
});
window.addEventListener("resize", function()
{
canvas.width = window.innerWidth - 20;
canvas.height = window.innerHeight - 20;
});
window.addEventListener("mousemove", function(e)
{
mouse.x = e.layerX || e.offsetX;
mouse.y = e.layerY || e.offsetY;
});
makeCircles();
update();
draw();
});
</script>
</body>
</html>

HTML5 Canvas flickering, don't think a buffer will work

Right now, I have a JavaScript function that goes through a 24x24 grid and draws a random color at each location. It works fine in Chrome, however there's this horrible flickering in Firefox. Now, normally it would seem like a buffer would fix it, however the canvas is actually invisible, and I just set it as the background using canvas.toDataURL(); once all the squares are updated. Any thoughts as to why it isn't working?
Edit:
Here's the code in question:
window.onload=function(){
var canvas = document.getElementById("background"); //canvas is assigned the ID of background
var canvasBuffer = canvas;
var ctx = canvas.getContext('2d');
window.requestAnimFrame = (function (callback) {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) {
window.setTimeout(callback, 1000 / 30);
};
})();
var fps = 30;
var ar = 0;
var ag = 0;
var ab = 0;
var br = 0;
var bg = 0;
var bb = 0;
var redmod = .6;
var greenmod = 1.1;
var bluemod = 1.2;
var count = 0;
var coords = new Array(8);
//create an 8x8 matrix of value pairs (current value and target value)
for (var i = 0; i < 24; i++) {
coords[i] = new Array(8);
for (var j = 0; j < 24; j++) {
coords[i][j] = new Array(2);
coords[i][j][0] = randomColor();
coords[i][j][1] = randomColor();
ctx.fillStyle = "#" + coords[i][j][0];
ctx.fillRect(i * 20, j * 20, 20, 20);
}
}
function assignToDiv(){ // sets the canvas as the background of two divs
dataUrl = canvas.toDataURL();
document.getElementById("intro").style.background='url('+dataUrl+')'
document.getElementById("nav").style.background='url('+dataUrl+')'
}
function animate() {
setTimeout(function () {
requestAnimFrame(animate);
requestAnimFrame(assignToDiv);
// Drawing code goes here
// changes the general color over time
if (count == 256){
redmod = .4;
greenmod = .8;
bluemod = 1;
}
else if (count == 512){
redmod = 1.4;
greenmod = .8;
bluemod = .6;
}
else if (count == 768){
redmod = 1.3;
greenmod = .5;
bluemod = .6;
}
else if (count == 1024){
redmod = 1.1;
greenmod = .3;
bluemod = .5;
}
else if (count == 1280){
redmod = .6;
greenmod = 1.1;
bluemod = 1.2;
count = 0;
}
count++;
//choose 3 squares to change
for (var m = 0; m < 3; m++){
var r = parseInt(Math.random() * 24);
var c = parseInt(Math.random() * 24);
coords[c][r][1] = randomColor();
}
//update each square
for (var k = 0; k < 24; k++) {
for (var l = 0; l < 24; l++) {
//make sure all colors are 6 digits long
while (coords[k][l][0].length<6){
coords[k][l][0]= "0" + coords[k][l][0];
}
while (coords[k][l][1].length<6){
coords[k][l][1]= "0" + coords[k][l][1];
}
//convert the red, green, and blue values back to dec
ar = parseInt(coords[k][l][0].substring(0,2),16);
ag = parseInt(coords[k][l][0].substring(2,4),16);
ab = parseInt(coords[k][l][0].substring(4,6),16)
br = parseInt(coords[k][l][1].substring(0,2),16);
bg = parseInt(coords[k][l][1].substring(2,4),16);
bb = parseInt(coords[k][l][1].substring(4,6),16);
//move the current color closer to the target color
ar = parseInt(ar + (br - ar)/fps);
ag = parseInt(ag + (bg - ag)/fps);
ab = parseInt(ab + (bb - ab)/fps);
//put it pack to hex
var RR = ((ar.toString(16).length==1)?"0"+ar.toString(16):ar.toString(16));
var GG = ((ag.toString(16).length==1)?"0"+ag.toString(16):ag.toString(16));
var BB = ((ab.toString(16).length==1)?"0"+ab.toString(16):ab.toString(16));
coords[k][l][0] = RR + GG + BB;
ctx.fillStyle = "#" + coords[k][l][0];
ctx.fillRect(k * 20, l * 20, 20, 20);
}
}
}, 1000 / fps);
}
// create a random color object {red,green,blue}
function randomColor() {
var red = Math.floor(Math.random() * 128 * redmod);
var green = Math.floor(Math.random() * 128 * greenmod);
var blue = Math.floor(Math.random() * 128 * bluemod);
var avg = red * .3 + green * .6 + blue * .1;
red = parseInt(red * .5 + avg * .5);
green = parseInt(green * .5 + avg * .5);
blue = parseInt(blue * .5 + avg * .5);
var redhex = ((red.toString(16).length==1)?"0"+red.toString(16):red.toString(16));
var greenhex = ((green.toString(16).length==1)?"0"+green.toString(16):green.toString(16));
var bluehex = ((blue.toString(16).length==1)?"0"+blue.toString(16):blue.toString(16));
return (redhex + greenhex + bluehex);
}
animate();
assignToDiv();
}
Then the canvas is placed at the top of the body:
<canvas id="background" width=480 height=480></canvas>
and in the CSS:
#background {
display:none;
}
Your problem is that you're replacing 2 of your element backgrounds at 60fps.
Instead of constantly replacing the background of "intro" and "nav", layer a canvas element under both intro & nav. Then draw on those underlying canvases.

Categories

Resources