I'm trying to wrap my head around the globalCompositeOperation property by attempting to combine these two examples: JSFiddle and Codepen.
The former is using destination-outand the latter is using source-over. Would it be possible to use the fiery cursor in the Codepen, but also have it remove the portion of the overlay fill that the user clicks on, as in the Fiddle?
Any assistance would be most appreciated. I can combine the demos on Codepen to use the same methods if necessary.
Relevant Fiddle code:
function drawDot(mouseX,mouseY){
bridgeCanvas.beginPath();
bridgeCanvas.arc(mouseX, mouseY, brushRadius, 0, 2*Math.PI, true);
bridgeCanvas.fillStyle = '#000';
bridgeCanvas.globalCompositeOperation = "destination-out";
bridgeCanvas.fill();
}
Relevant Codepen code:
Fire.prototype.clearCanvas = function(){
this.ctx.globalCompositeOperation = "source-over";
this.ctx.fillStyle = "rgba( 15, 5, 2, 1 )";
this.ctx.fillRect( 0, 0, window.innerWidth, window.innerHeight );
this.ctx.globalCompositeOperation = "lighter";
this.ctx.rect(0, 0, this.canvas.width, this.canvas.height);
this.ctx.fillStyle = this.pattern;
this.ctx.fill();/**/
}
As I said in comments, you'll have to divide your code in at least two parts.
The cropper function uses "destination-out" compositing operation to remove the already drawn pixels of the canvas where the new ones should be drawn. In your version, it uses a background-image, and once the foreground pixels are removed, you can see this background since in the now transparent areas of the canvas.
The flame one in the other hand, uses '"lighter"', "color-dodge" and "soft-light" blending operations. This will add the colors of both the already there and the new drawn pixels.
At least the first one, if used on a transparent area, will be the same as the default "source-over" composite operation. So you need to have the background image drawn onto the canvas to be able to use it in the blending.
For this, you've got to use a second, off-screen canvas, where you will only apply the eraser "destination-out" operation. Then, on the visible canvas, at each new eraser frame, you'll have to draw the background image on your visible canvas, then the eraser one, with the holes, and finally the blending one, which will mix all together.
Here is a quick code dump, where I rewrote a bit the eraser, and modified the Fire one, in order to make our main function handles both events and animation loop.
function MainDrawing(){
this.canvas = document.getElementById('main');
this.ctx = this.canvas.getContext('2d');
this.background = new Image();
this.background.src = "https://s3-us-west-2.amazonaws.com/s.cdpn.io/4273/calgary-bridge-1943.jpg"
this.eraser = new Eraser(this.canvas);
this.fire = new Fire(this.canvas);
this.attachEvents();
}
MainDrawing.prototype = {
anim: function(){
if(this.stopped)
return;
this.ctx.globalCompositeOperation = 'source-over';
this.ctx.drawImage(this.background, 0,0);
this.ctx.drawImage(this.eraser.canvas, 0,0);
this.fire.run();
requestAnimationFrame(this.anim.bind(this));
},
stop: function(){
this.stopped = true;
},
attachEvents: function(){
var mouseDown = false;
this.canvas.onmousedown = function(){
mouseDown = true;
};
this.canvas.onmouseup = function(){
mouseDown = false;
};
this.canvas.onmousemove = function(e){
if(mouseDown){
this.eraser.handleClick(e);
}
this.fire.updateMouse(e);
}.bind(this);
}
};
function Eraser(canvas){
this.main = canvas;
this.canvas = canvas.cloneNode();
var ctx = this.ctx = this.canvas.getContext('2d');
this.img = new Image();
this.img.onload = function(){
ctx.drawImage(this, 0, 0);
ctx.globalCompositeOperation = 'destination-out';
};
this.img.src = "https://s3-us-west-2.amazonaws.com/s.cdpn.io/4273/calgary-bridge-2013.jpg";
this.getRect();
}
Eraser.prototype = {
getRect: function(){
this.rect = this.main.getBoundingClientRect();
},
handleClick: function(evt){
var x = evt.clientX - this.rect.left;
var y = evt.clientY - this.rect.top;
this.draw(x,y);
},
draw: function(x, y){
this.ctx.beginPath();
this.ctx.arc(x, y, 30, 0, Math.PI*2);
this.ctx.fill();
}
};
var Fire = function(canvas){
this.canvas = canvas;
this.ctx = this.canvas.getContext('2d');
this.aFires = [];
this.aSpark = [];
this.aSpark2 = [];
this.mouse = {
x : this.canvas.width * .5,
y : this.canvas.height * .75,
}
}
Fire.prototype.run = function(){
this.update();
this.draw();
}
Fire.prototype.start = function(){
this.bRuning = true;
this.run();
}
Fire.prototype.stop = function(){
this.bRuning = false;
}
Fire.prototype.update = function(){
this.aFires.push( new Flame( this.mouse ) );
this.aSpark.push( new Spark( this.mouse ) );
this.aSpark2.push( new Spark( this.mouse ) );
for (var i = this.aFires.length - 1; i >= 0; i--) {
if( this.aFires[i].alive )
this.aFires[i].update();
else
this.aFires.splice( i, 1 );
}
for (var i = this.aSpark.length - 1; i >= 0; i--) {
if( this.aSpark[i].alive )
this.aSpark[i].update();
else
this.aSpark.splice( i, 1 );
}
for (var i = this.aSpark2.length - 1; i >= 0; i--) {
if( this.aSpark2[i].alive )
this.aSpark2[i].update();
else
this.aSpark2.splice( i, 1 );
}
}
Fire.prototype.draw = function(){
this.drawHalo();
this.ctx.globalCompositeOperation = "overlay";//or lighter or soft-light
for (var i = this.aFires.length - 1; i >= 0; i--) {
this.aFires[i].draw( this.ctx );
}
this.ctx.globalCompositeOperation = "soft-light";//"soft-light";//"color-dodge";
for (var i = this.aSpark.length - 1; i >= 0; i--) {
if( ( i % 2 ) === 0 )
this.aSpark[i].draw( this.ctx );
}
this.ctx.globalCompositeOperation = "color-dodge";//"soft-light";//"color-dodge";
for (var i = this.aSpark2.length - 1; i >= 0; i--) {
this.aSpark2[i].draw( this.ctx );
}
}
Fire.prototype.updateMouse = function( e ){
this.mouse.x = e.clientX;
this.mouse.y = e.clientY;
}
Fire.prototype.drawHalo = function(){
var r = rand( 300, 350 );
this.ctx.globalCompositeOperation = "lighter";
this.grd = this.ctx.createRadialGradient( this.mouse.x, this.mouse.y,r,this.mouse.x, this.mouse.y, 0 );
this.grd.addColorStop(0,"transparent");
this.grd.addColorStop(1,"rgb( 50, 2, 0 )");
this.ctx.beginPath();
this.ctx.arc( this.mouse.x, this.mouse.y - 100, r, 0, 2*Math.PI );
this.ctx.fillStyle= this.grd;
this.ctx.fill();
}
var Flame = function( mouse ){
this.cx = mouse.x;
this.cy = mouse.y;
this.x = rand( this.cx - 25, this.cx + 25);
this.y = rand( this.cy - 5, this.cy + 5);
this.lx = this.x;
this.ly = this.y;
this.vy = rand( 1, 3 );
this.vx = rand( -1, 1 );
this.r = rand( 30, 40 );
this.life = rand( 2, 7 );
this.alive = true;
this.c = {
h : Math.floor( rand( 2, 40) ),
s : 100,
l : rand( 80, 100 ),
a : 0,
ta : rand( 0.8, 0.9 )
}
}
Flame.prototype.update = function()
{
this.lx = this.x;
this.ly = this.y;
this.y -= this.vy;
this.vy += 0.08;
this.x += this.vx;
if( this.x < this.cx )
this.vx += 0.2;
else
this.vx -= 0.2;
if( this.r > 0 )
this.r -= 0.3;
if( this.r <= 0 )
this.r = 0;
this.life -= 0.12;
if( this.life <= 0 ){
this.c.a -= 0.05;
if( this.c.a <= 0 )
this.alive = false;
}else if( this.life > 0 && this.c.a < this.c.ta ){
this.c.a += .08;
}
}
Flame.prototype.draw = function( ctx ){
this.grd1 = ctx.createRadialGradient( this.x, this.y, this.r*3, this.x, this.y, 0 );
this.grd1.addColorStop( 0.5, "hsla( " + this.c.h + ", " + this.c.s + "%, " + this.c.l + "%, " + (this.c.a/20) + ")" );
this.grd1.addColorStop( 0, "transparent" );
this.grd2 = ctx.createRadialGradient( this.x, this.y, this.r, this.x, this.y, 0 );
this.grd2.addColorStop( 0.5, "hsla( " + this.c.h + ", " + this.c.s + "%, " + this.c.l + "%, " + this.c.a + ")" );
this.grd2.addColorStop( 0, "transparent" );
ctx.beginPath();
ctx.arc( this.x, this.y, this.r * 3, 0, 2*Math.PI );
ctx.fillStyle = this.grd1;
//ctx.fillStyle = "hsla( " + this.c.h + ", " + this.c.s + "%, " + this.c.l + "%, " + (this.c.a/20) + ")";
ctx.fill();
ctx.globalCompositeOperation = "overlay";
ctx.beginPath();
ctx.arc( this.x, this.y, this.r, 0, 2*Math.PI );
ctx.fillStyle = this.grd2;
ctx.fill();
ctx.beginPath();
ctx.moveTo( this.lx , this.ly);
ctx.lineTo( this.x, this.y);
ctx.strokeStyle = "hsla( " + this.c.h + ", " + this.c.s + "%, " + this.c.l + "%, 1)";
ctx.lineWidth = rand( 1, 2 );
ctx.stroke();
ctx.closePath();
}
var Spark = function( mouse ){
this.cx = mouse.x;
this.cy = mouse.y;
this.x = rand( this.cx -40, this.cx + 40);
this.y = rand( this.cy, this.cy + 5);
this.lx = this.x;
this.ly = this.y;
this.vy = rand( 1, 3 );
this.vx = rand( -4, 4 );
this.r = rand( 0, 1 );
this.life = rand( 4, 8 );
this.alive = true;
this.c = {
h : Math.floor( rand( 2, 40) ),
s : 100,
l : rand( 40, 100 ),
a : rand( 0.8, 0.9 )
}
}
Spark.prototype.update = function()
{
this.lx = this.x;
this.ly = this.y;
this.y -= this.vy;
this.x += this.vx;
if( this.x < this.cx )
this.vx += 0.2;
else
this.vx -= 0.2;
this.vy += 0.08;
this.life -= 0.1;
if( this.life <= 0 ){
this.c.a -= 0.05;
if( this.c.a <= 0 )
this.alive = false;
}
}
Spark.prototype.draw = function( ctx ){
ctx.beginPath();
ctx.moveTo( this.lx , this.ly);
ctx.lineTo( this.x, this.y);
ctx.strokeStyle = "hsla( " + this.c.h + ", " + this.c.s + "%, " + this.c.l + "%, " + (this.c.a / 2) + ")";
ctx.lineWidth = this.r * 2;
ctx.lineCap = 'round';
ctx.stroke();
ctx.closePath();
ctx.beginPath();
ctx.moveTo( this.lx , this.ly);
ctx.lineTo( this.x, this.y);
ctx.strokeStyle = "hsla( " + this.c.h + ", " + this.c.s + "%, " + this.c.l + "%, " + this.c.a + ")";
ctx.lineWidth = this.r;
ctx.stroke();
ctx.closePath();
}
rand = function( min, max ){ return Math.random() * ( max - min) + min; };
var app = new MainDrawing();
app.anim();
<canvas id="main" width="750" height="465"></canvas>
Related
i'm trying to use this Fireworks javascript. But when i write some text in the middle of the HTML page, the fireworks will not go over it and is limited by the text position...how can i override this and keep the fireworks go up to the top of the page ?
i tryed to fix the SCREEN_WIDTH and SCREEN_HEIGHT position but it doesn't work...
var SCREEN_WIDTH = window.innerWidth,
SCREEN_HEIGHT = window.innerHeight,
mousePos = {
x: 400,
y: 300
},
// create canvas
canvas = document.createElement('canvas'),
context = canvas.getContext('2d'),
particles = [],
rockets = [],
MAX_PARTICLES = 400,
colorCode = 0;
// init
$(document).ready(function() {
document.body.appendChild(canvas);
canvas.width = SCREEN_WIDTH;
canvas.height = SCREEN_HEIGHT;
setInterval(launch, 800);
setInterval(loop, 1000 / 50);
});
// update mouse position
$(document).mousemove(function(e) {
e.preventDefault();
mousePos = {
x: e.clientX,
y: e.clientY
};
});
// launch more rockets!!!
$(document).mousedown(function(e) {
for (var i = 0; i < 5; i++) {
launchFrom(Math.random() * SCREEN_WIDTH * 2 / 3 + SCREEN_WIDTH / 6);
}
});
function launch() {
launchFrom(mousePos.x);
}
function launchFrom(x) {
if (rockets.length < 10) {
var rocket = new Rocket(x);
rocket.explosionColor = Math.floor(Math.random() * 360 / 10) * 10;
rocket.vel.y = Math.random() * -3 - 4;
rocket.vel.x = Math.random() * 6 - 3;
rocket.size = 8;
rocket.shrink = 0.999;
rocket.gravity = 0.01;
rockets.push(rocket);
}
}
function loop() {
// update screen size
if (SCREEN_WIDTH != window.innerWidth) {
canvas.width = SCREEN_WIDTH = window.innerWidth;
}
if (SCREEN_HEIGHT != window.innerHeight) {
canvas.height = SCREEN_HEIGHT = window.innerHeight;
}
// clear canvas
context.fillStyle = "rgba(0, 0, 0, 0.05)";
context.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
var existingRockets = [];
for (var i = 0; i < rockets.length; i++) {
// update and render
rockets[i].update();
rockets[i].render(context);
// calculate distance with Pythagoras
var distance = Math.sqrt(Math.pow(mousePos.x - rockets[i].pos.x, 2) + Math.pow(mousePos.y - rockets[i].pos.y, 2));
// random chance of 1% if rockets is above the middle
var randomChance = rockets[i].pos.y < (SCREEN_HEIGHT * 2 / 3) ? (Math.random() * 100 <= 1) : false;
/* Explosion rules
- 80% of screen
- going down
- close to the mouse
- 1% chance of random explosion
*/
if (rockets[i].pos.y < SCREEN_HEIGHT / 5 || rockets[i].vel.y >= 0 || distance < 50 || randomChance) {
rockets[i].explode();
} else {
existingRockets.push(rockets[i]);
}
}
rockets = existingRockets;
var existingParticles = [];
for (var i = 0; i < particles.length; i++) {
particles[i].update();
// render and save particles that can be rendered
if (particles[i].exists()) {
particles[i].render(context);
existingParticles.push(particles[i]);
}
}
// update array with existing particles - old particles should be garbage collected
particles = existingParticles;
while (particles.length > MAX_PARTICLES) {
particles.shift();
}
}
function Particle(pos) {
this.pos = {
x: pos ? pos.x : 0,
y: pos ? pos.y : 0
};
this.vel = {
x: 0,
y: 0
};
this.shrink = .97;
this.size = 2;
this.resistance = 1;
this.gravity = 0;
this.flick = false;
this.alpha = 1;
this.fade = 0;
this.color = 0;
}
Particle.prototype.update = function() {
// apply resistance
this.vel.x *= this.resistance;
this.vel.y *= this.resistance;
// gravity down
this.vel.y += this.gravity;
// update position based on speed
this.pos.x += this.vel.x;
this.pos.y += this.vel.y;
// shrink
this.size *= this.shrink;
// fade out
this.alpha -= this.fade;
};
Particle.prototype.render = function(c) {
if (!this.exists()) {
return;
}
c.save();
c.globalCompositeOperation = 'lighter';
var x = this.pos.x,
y = this.pos.y,
r = this.size / 2;
var gradient = c.createRadialGradient(x, y, 0.1, x, y, r);
gradient.addColorStop(0.1, "rgba(255,255,255," + this.alpha + ")");
gradient.addColorStop(0.8, "hsla(" + this.color + ", 100%, 50%, " + this.alpha + ")");
gradient.addColorStop(1, "hsla(" + this.color + ", 100%, 50%, 0.1)");
c.fillStyle = gradient;
c.beginPath();
c.arc(this.pos.x, this.pos.y, this.flick ? Math.random() * this.size : this.size, 0, Math.PI * 2, true);
c.closePath();
c.fill();
c.restore();
};
Particle.prototype.exists = function() {
return this.alpha >= 0.1 && this.size >= 1;
};
function Rocket(x) {
Particle.apply(this, [{
x: x,
y: SCREEN_HEIGHT}]);
this.explosionColor = 0;
}
Rocket.prototype = new Particle();
Rocket.prototype.constructor = Rocket;
Rocket.prototype.explode = function() {
var count = Math.random() * 10 + 80;
for (var i = 0; i < count; i++) {
var particle = new Particle(this.pos);
var angle = Math.random() * Math.PI * 2;
// emulate 3D effect by using cosine and put more particles in the middle
var speed = Math.cos(Math.random() * Math.PI / 2) * 15;
particle.vel.x = Math.cos(angle) * speed;
particle.vel.y = Math.sin(angle) * speed;
particle.size = 10;
particle.gravity = 0.2;
particle.resistance = 0.92;
particle.shrink = Math.random() * 0.05 + 0.93;
particle.flick = true;
particle.color = this.explosionColor;
particles.push(particle);
}
};
Rocket.prototype.render = function(c) {
if (!this.exists()) {
return;
}
c.save();
c.globalCompositeOperation = 'lighter';
var x = this.pos.x,
y = this.pos.y,
r = this.size / 2;
var gradient = c.createRadialGradient(x, y, 0.1, x, y, r);
gradient.addColorStop(0.1, "rgba(255, 255, 255 ," + this.alpha + ")");
gradient.addColorStop(1, "rgba(0, 0, 0, " + this.alpha + ")");
c.fillStyle = gradient;
c.beginPath();
c.arc(this.pos.x, this.pos.y, this.flick ? Math.random() * this.size / 2 + this.size / 2 : this.size, 0, Math.PI * 2, true);
c.closePath();
c.fill();
c.restore();
};
body {
background-color: #000000;
margin: 0px;
overflow: hidden;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Fireworks!</title>
</head>
<body/>
</html>
The problem is the canvas used by the script is positioned relative by default. To make it always visible completely on screen we have to make it fixed and set the top and left CSS values to 0.
Now because its fixed the canvas renders on top of everything. To get it in the background set z-index to -1.
All additions together:
canvas.style.position="fixed";
canvas.style.top="0";
canvas.style.left="0";
canvas.style.zIndex="-1";
Complete Source:
var SCREEN_WIDTH = window.innerWidth,
SCREEN_HEIGHT = window.innerHeight,
mousePos = {
x: 400,
y: 300
},
// create canvas
canvas = document.createElement('canvas'),
context = canvas.getContext('2d'),
particles = [],
rockets = [],
MAX_PARTICLES = 400,
colorCode = 0;
// init
$(document).ready(function() {
document.body.appendChild(canvas);
canvas.width = SCREEN_WIDTH;
canvas.height = SCREEN_HEIGHT;
canvas.style.position = "fixed";
canvas.style.top = "0";
canvas.style.left = "0";
canvas.style.zIndex = "-1";
setInterval(launch, 800);
setInterval(loop, 1000 / 50);
});
// update mouse position
$(document).mousemove(function(e) {
e.preventDefault();
mousePos = {
x: e.clientX,
y: e.clientY
};
});
// launch more rockets!!!
$(document).mousedown(function(e) {
for (var i = 0; i < 5; i++) {
launchFrom(Math.random() * SCREEN_WIDTH * 2 / 3 + SCREEN_WIDTH / 6);
}
});
function launch() {
launchFrom(mousePos.x);
}
function launchFrom(x) {
if (rockets.length < 10) {
var rocket = new Rocket(x);
rocket.explosionColor = Math.floor(Math.random() * 360 / 10) * 10;
rocket.vel.y = Math.random() * -3 - 4;
rocket.vel.x = Math.random() * 6 - 3;
rocket.size = 8;
rocket.shrink = 0.999;
rocket.gravity = 0.01;
rockets.push(rocket);
}
}
function loop() {
// update screen size
if (SCREEN_WIDTH != window.innerWidth) {
canvas.width = SCREEN_WIDTH = window.innerWidth;
}
if (SCREEN_HEIGHT != window.innerHeight) {
canvas.height = SCREEN_HEIGHT = window.innerHeight;
}
// clear canvas
context.fillStyle = "rgba(0, 0, 0, 0.05)";
context.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
var existingRockets = [];
for (var i = 0; i < rockets.length; i++) {
// update and render
rockets[i].update();
rockets[i].render(context);
// calculate distance with Pythagoras
var distance = Math.sqrt(Math.pow(mousePos.x - rockets[i].pos.x, 2) + Math.pow(mousePos.y - rockets[i].pos.y, 2));
// random chance of 1% if rockets is above the middle
var randomChance = rockets[i].pos.y < (SCREEN_HEIGHT * 2 / 3) ? (Math.random() * 100 <= 1) : false;
/* Explosion rules
- 80% of screen
- going down
- close to the mouse
- 1% chance of random explosion
*/
if (rockets[i].pos.y < SCREEN_HEIGHT / 5 || rockets[i].vel.y >= 0 || distance < 50 || randomChance) {
rockets[i].explode();
} else {
existingRockets.push(rockets[i]);
}
}
rockets = existingRockets;
var existingParticles = [];
for (var i = 0; i < particles.length; i++) {
particles[i].update();
// render and save particles that can be rendered
if (particles[i].exists()) {
particles[i].render(context);
existingParticles.push(particles[i]);
}
}
// update array with existing particles - old particles should be garbage collected
particles = existingParticles;
while (particles.length > MAX_PARTICLES) {
particles.shift();
}
}
function Particle(pos) {
this.pos = {
x: pos ? pos.x : 0,
y: pos ? pos.y : 0
};
this.vel = {
x: 0,
y: 0
};
this.shrink = .97;
this.size = 2;
this.resistance = 1;
this.gravity = 0;
this.flick = false;
this.alpha = 1;
this.fade = 0;
this.color = 0;
}
Particle.prototype.update = function() {
// apply resistance
this.vel.x *= this.resistance;
this.vel.y *= this.resistance;
// gravity down
this.vel.y += this.gravity;
// update position based on speed
this.pos.x += this.vel.x;
this.pos.y += this.vel.y;
// shrink
this.size *= this.shrink;
// fade out
this.alpha -= this.fade;
};
Particle.prototype.render = function(c) {
if (!this.exists()) {
return;
}
c.save();
c.globalCompositeOperation = 'lighter';
var x = this.pos.x,
y = this.pos.y,
r = this.size / 2;
var gradient = c.createRadialGradient(x, y, 0.1, x, y, r);
gradient.addColorStop(0.1, "rgba(255,255,255," + this.alpha + ")");
gradient.addColorStop(0.8, "hsla(" + this.color + ", 100%, 50%, " + this.alpha + ")");
gradient.addColorStop(1, "hsla(" + this.color + ", 100%, 50%, 0.1)");
c.fillStyle = gradient;
c.beginPath();
c.arc(this.pos.x, this.pos.y, this.flick ? Math.random() * this.size : this.size, 0, Math.PI * 2, true);
c.closePath();
c.fill();
c.restore();
};
Particle.prototype.exists = function() {
return this.alpha >= 0.1 && this.size >= 1;
};
function Rocket(x) {
Particle.apply(this, [{
x: x,
y: SCREEN_HEIGHT
}]);
this.explosionColor = 0;
}
Rocket.prototype = new Particle();
Rocket.prototype.constructor = Rocket;
Rocket.prototype.explode = function() {
var count = Math.random() * 10 + 80;
for (var i = 0; i < count; i++) {
var particle = new Particle(this.pos);
var angle = Math.random() * Math.PI * 2;
// emulate 3D effect by using cosine and put more particles in the middle
var speed = Math.cos(Math.random() * Math.PI / 2) * 15;
particle.vel.x = Math.cos(angle) * speed;
particle.vel.y = Math.sin(angle) * speed;
particle.size = 10;
particle.gravity = 0.2;
particle.resistance = 0.92;
particle.shrink = Math.random() * 0.05 + 0.93;
particle.flick = true;
particle.color = this.explosionColor;
particles.push(particle);
}
};
Rocket.prototype.render = function(c) {
if (!this.exists()) {
return;
}
c.save();
c.globalCompositeOperation = 'lighter';
var x = this.pos.x,
y = this.pos.y,
r = this.size / 2;
var gradient = c.createRadialGradient(x, y, 0.1, x, y, r);
gradient.addColorStop(0.1, "rgba(255, 255, 255 ," + this.alpha + ")");
gradient.addColorStop(1, "rgba(0, 0, 0, " + this.alpha + ")");
c.fillStyle = gradient;
c.beginPath();
c.arc(this.pos.x, this.pos.y, this.flick ? Math.random() * this.size / 2 + this.size / 2 : this.size, 0, Math.PI * 2, true);
c.closePath();
c.fill();
c.restore();
};
body {
background-color: #000000;
margin: 0px;
overflow: hidden;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Fireworks!</title>
</head>
<body>
<p>
test
</p>
<br />
<p>
test2
</p>
</body>
</html>
I have a canvas
(check in Chrome only)
$(function() {
var Fire = function(){
this.canvas = document.getElementById('fire');
this.ctx = this.canvas.getContext('2d');
this.canvas.height = window.innerHeight;
this.canvas.width = window.innerWidth;
this.aFires = [];
this.aSpark = [];
this.aSpark2 = [];
this.mouse = {
x : this.canvas.width * .5,
y : this.canvas.height * .75,
}
this.init();
}
Fire.prototype.init = function()
{
//this.canvas.addEventListener('mousemove', this.updateMouse.bind( this ), false);
}
Fire.prototype.run = function(){
this.update();
this.draw();
if( this.bRuning )
requestAnimationFrame( this.run.bind( this ) );
}
Fire.prototype.start = function(){
this.bRuning = true;
this.run();
}
Fire.prototype.update = function(){
this.aFires.push( new Flame( this.mouse ) );
this.aSpark.push( new Spark( this.mouse ) );
this.aSpark2.push( new Spark( this.mouse ) );
for (var i = this.aFires.length - 1; i >= 0; i--) {
if( this.aFires[i].alive )
this.aFires[i].update();
else
this.aFires.splice( i, 1 );
}
for (var i = this.aSpark.length - 1; i >= 0; i--) {
if( this.aSpark[i].alive )
this.aSpark[i].update();
else
this.aSpark.splice( i, 1 );
}
for (var i = this.aSpark2.length - 1; i >= 0; i--) {
if( this.aSpark2[i].alive )
this.aSpark2[i].update();
else
this.aSpark2.splice( i, 1 );
}
}
Fire.prototype.draw = function(){
this.ctx.globalCompositeOperation = "source-over";
this.ctx.fillStyle = "rgba( 15, 5, 2, 1 )";
this.ctx.fillRect( 0, 0, window.innerWidth, window.innerHeight );
this.grd = this.ctx.createRadialGradient( this.mouse.x, this.mouse.y - 200,200,this.mouse.x, this.mouse.y - 100,0 );
this.grd.addColorStop(0,"rgb( 15, 5, 2 )");
this.grd.addColorStop(1,"rgb(30, 10, 2 )");
this.ctx.beginPath();
this.ctx.arc( this.mouse.x, this.mouse.y - 100, 200, 0, 2*Math.PI );
this.ctx.fillStyle= this.grd;
this.ctx.fill();
this.ctx.globalCompositeOperation = "overlay";//or lighter or soft-light
for (var i = this.aFires.length - 1; i >= 0; i--) {
this.aFires[i].draw( this.ctx );
}
this.ctx.globalCompositeOperation = "soft-light";//"soft-light";//"color-dodge";
for (var i = this.aSpark.length - 1; i >= 0; i--) {
if( ( i % 2 ) === 0 )
this.aSpark[i].draw( this.ctx );
}
this.ctx.globalCompositeOperation = "color-dodge";//"soft-light";//"color-dodge";
for (var i = this.aSpark2.length - 1; i >= 0; i--) {
this.aSpark2[i].draw( this.ctx );
}
}
// Flame
var Flame = function( mouse ){
this.cx = mouse.x;
this.cy = mouse.y;
this.x = rand( this.cx - 600, this.cx + 600);
this.y = rand( this.cy - -200, this.cy + 5);
this.vy = rand( 1, 3 );
this.vx = rand( -1, 1 );
this.r = rand( 20, 30 );
this.life = rand( 3, 6 );
this.alive = true;
this.c = {
h : Math.floor( rand( 2, 40) ),
s : 1000,
l : rand( 80, 100 ),
a : 0,
ta : rand( 0.8, 0.9 )
}
}
Flame.prototype.update = function()
{
this.y -= this.vy;
this.vy += 0.05;
this.x += this.vx;
if( this.x < this.cx )
this.vx += 0.1;
else
this.vx -= 0.1;
if( this.r > 0 )
this.r -= 0.1;
if( this.r <= 0 )
this.r = 0;
this.life -= 0.15;
if( this.life <= 0 ){
this.c.a -= 0.05;
if( this.c.a <= 0 )
this.alive = false;
}else if( this.life > 0 && this.c.a < this.c.ta ){
this.c.a += .08;
}
}
Flame.prototype.draw = function( ctx ){
ctx.beginPath();
ctx.arc( this.x, this.y, this.r * 3, 0, 2*Math.PI );
ctx.fillStyle = "hsla( " + this.c.h + ", " + this.c.s + "%, " + this.c.l + "%, " + (this.c.a/20) + ")";
ctx.fill();
ctx.beginPath();
ctx.arc( this.x, this.y, this.r, 0, 2*Math.PI );
ctx.fillStyle = "hsla( " + this.c.h + ", " + this.c.s + "%, " + this.c.l + "%, " + this.c.a + ")";
ctx.fill();
}
// Spark
var Spark = function( mouse ){
this.cx = mouse.x;
this.cy = mouse.y;
this.x = rand( this.cx -600, this.cx + 600);
this.y = rand( this.cy - -200, this.cy + 5);
this.lx = this.x;
this.ly = this.y;
<!-- Edit Value -->
this.vy = rand( 1, 3 );
this.vx = rand( -4, 4 );
this.r = rand( 0, 1 );
this.life = rand( 4, 5 );
this.alive = true;
this.c = {
h : Math.floor( rand( 2, 40) ),
s : 100,
l : rand( 40, 100 ),
a : rand( 0.8, 0.9 )
}
}
Spark.prototype.update = function()
{
this.lx = this.x;
this.ly = this.y;
this.y -= this.vy;
this.x += this.vx;
if( this.x < this.cx )
this.vx += 0.2;
else
this.vx -= 0.2;
this.vy += 0.08;
this.life -= 0.1;
if( this.life <= 0 ){
this.c.a -= 0.05;
if( this.c.a <= 0 )
this.alive = false;
}
}
Spark.prototype.draw = function( ctx ){
ctx.beginPath();
ctx.moveTo( this.lx , this.ly);
ctx.lineTo( this.x, this.y);
ctx.strokeStyle = "hsla( " + this.c.h + ", " + this.c.s + "%, " + this.c.l + "%, " + (this.c.a / 2) + ")";
ctx.lineWidth = this.r * 2;
ctx.lineCap = 'round';
ctx.stroke();
ctx.closePath();
ctx.beginPath();
ctx.moveTo( this.lx , this.ly);
ctx.lineTo( this.x, this.y);
ctx.strokeStyle = "hsla( " + this.c.h + ", " + this.c.s + "%, " + this.c.l + "%, " + this.c.a + ")";
ctx.lineWidth = this.r;
ctx.stroke();
ctx.closePath();
}
rand = function( min, max ){ return Math.random() * ( max - min) + min; };
onresize = function () { oCanvas.canvas.width = window.innerWidth; oCanvas.canvas.height = window.innerHeight; };
var oCanvas;
init = function()
{
oCanvas = new Fire();
oCanvas.start();
}
window.onload = init;
});
h1
{
position:relative;
z-index:1;
float:right;
width:100%;
color:#fff;
}
#fire
{
position:fixed;
height:100%;
top:0;
right:0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.2.3/jquery.min.js"></script>
<canvas id="fire"></canvas>
<h1>Hover Me!</h1>
I want to change the amount of canvas every time the Hover Me! is hoverd.
For example, this value is this.vy = rand (1, 3); To this value this.vy = rand (1, 30); When the hover changes.
Another thing to note is why the Mozilla browser is extremely difficult to execute and the colors in the Internet Explorer are not supported.
please help me!
I've looked at a lot of examples -- and borrowed from some -- and can't seem to get this to work right. What I want is for the raycaster in onDocumentMouseDown to pick up sprites when the user clicks anywhere on the visible surface of a sprite. What I'm getting is a misaligned result, in that a sprite may be picked up if the user clicks somewhat to the right, above, or below the sprite, and will not pick it up at all if the user clicks on the left edge of the sprite. So basically something is misaligned, and I am at a loss for figuring out what I am doing wrong. Any guidance would be appreciated.
<script src="/common/three.js"></script>
<script src="/common/Detector.js"></script>
<script src="/common/CanvasRenderer.js"></script>
<script src="/common/GeometryUtils.js"></script>
<script src="/common/OrbitControls.js"></script>
<div id="WebGLCanvas"></div>
<script>
var container, scene, camera, renderer, controls;
var keyboard;
</script>
<script>
// custom global variables
var mouse = { x: 0, y: 0 };
var raycaster;
var sprites = new Array();
init();
try {
for (i = 0; i < 10; i++) {
var text = "Text " + i;
var x = Math.random() * 100;
var y = Math.random() * 100;
var z = Math.random() * 100;
var spritey = addOrUPdateSprite(text, i, x, y, z);
}
}
catch (ex) {
alert("error when creating sprite: " + ex.message);
}
animate();
function init() {
try {
scene = new THREE.Scene();
// CAMERA
var cont = document.getElementById("WebGLCanvas");
var SCREEN_WIDTH = window.innerWidth;
OFFSET_TOP = document.getElementById("WebGLCanvas").getBoundingClientRect().top;
var SCREEN_HEIGHT = window.innerHeight - OFFSET_TOP; //; //-document.getElementById("upper").clientHeight;
var VIEW_ANGLE = 60;
var ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT;
var NEAR = 0.1;
var FAR = 1000;
camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR);
scene.add(camera);
camera.position.set(0, 100, 200);
camera.lookAt(new THREE.Vector3());
renderer = new THREE.WebGLRenderer({ antialias: true });
container = document.getElementById('WebGLCanvas');
container.appendChild(renderer.domElement);
renderer.setSize(window.innerWidth, SCREEN_HEIGHT);
controls = new THREE.OrbitControls(camera, renderer.domElement);
// spritey.position.normalize();
raycaster = new THREE.Raycaster();
document.addEventListener('mousedown', onDocumentMouseDown, false);
document.addEventListener('touchstart', onDocumentTouchStart, false);
}
catch (ex) {
alert("error " + ex.message);
}
}
function animate() {
requestAnimationFrame(animate);
render();
update();
}
function update() {
controls.update();
}
function render() {
renderer.render(scene, camera);
}
function addOrUPdateSprite(text, name, x, y, z) {
var sprite = scene.getObjectByName(name);
if (sprite == null) {
sprite = makeTextSprite(text, { fontsize: 36, borderColor: { r: 255, g: 0, b: 0, a: 1.0 }, backgroundColor: { r: 255, g: 100, b: 100, a: 0.8 } });
sprite.name = name;
sprites.push(sprite);
scene.add(sprite);
}
sprite.position.set(x, y, z);
}
function makeTextSprite(message, parameters) {
if (parameters === undefined) parameters = {};
var fontface = parameters.hasOwnProperty("fontface") ? parameters["fontface"] : "sans-serif";
var fontsize = parameters.hasOwnProperty("fontsize") ? parameters["fontsize"] : 36;
var borderThickness = parameters.hasOwnProperty("borderThickness") ? parameters["borderThickness"] : 1;
var borderColor = parameters.hasOwnProperty("borderColor") ? parameters["borderColor"] : { r: 0, g: 0, b: 0, a: 1.0 };
var backgroundColor = parameters.hasOwnProperty("backgroundColor") ? parameters["backgroundColor"] : { r: 255, g: 255, b: 255, a: 1.0 };
var textColor = parameters.hasOwnProperty("textColor") ? parameters["textColor"] : { r: 0, g: 0, b: 0, a: 1.0 };
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
context.font = fontsize + "px " + fontface;
var metrics = context.measureText(message);
var textWidth = metrics.width;
context.fillStyle = "rgba(" + backgroundColor.r + "," + backgroundColor.g + "," + backgroundColor.b + "," + backgroundColor.a + ")";
context.strokeStyle = "rgba(" + borderColor.r + "," + borderColor.g + "," + borderColor.b + "," + borderColor.a + ")";
context.lineWidth = borderThickness;
roundRect(context, borderThickness / 2, borderThickness / 2, (textWidth + borderThickness) * 1.1, fontsize * 1.4 + borderThickness, 8);
context.fillStyle = "rgba(" + textColor.r + ", " + textColor.g + ", " + textColor.b + ", 1.0)";
context.fillText(message, borderThickness, fontsize + borderThickness);
var texture = new THREE.Texture(canvas)
texture.needsUpdate = true;
var spriteMaterial = new THREE.SpriteMaterial({ map: texture, useScreenCoordinates: false });
var sprite = new THREE.Sprite(spriteMaterial);
sprite.scale.set(1.0 * fontsize, 0.5 * fontsize, 1.5 * fontsize);
return sprite;
}
// function for drawing rounded rectangles
function roundRect(ctx, x, y, w, h, r) {
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.lineTo(x + w - r, y);
ctx.quadraticCurveTo(x + w, y, x + w, y + r);
ctx.lineTo(x + w, y + h - r);
ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
ctx.lineTo(x + r, y + h);
ctx.quadraticCurveTo(x, y + h, x, y + h - r);
ctx.lineTo(x, y + r);
ctx.quadraticCurveTo(x, y, x + r, y);
ctx.closePath();
ctx.fill();
ctx.stroke();
}
function onDocumentTouchStart(event) {
event.preventDefault();
event.clientX = event.touches[0].clientX;
event.clientY = event.touches[0].clientY;
onDocumentMouseDown(event);
}
function onDocumentMouseDown(event) {
mouse.x = (event.clientX / renderer.domElement.clientWidth) * 2 - 1;
mouse.y = -((event.clientY) / renderer.domElement.clientHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
var intersects = raycaster.intersectObjects(sprites, true);
if (intersects.length > 0) {
var obj = intersects[0].object;
alert(obj.name);
event.preventDefault();
}
}
</script>
In your makeTextSprite() function, after
var textWidth = metrics.width;
add this
context.strokeStyle = "white";
context.lineWidth = 5;
context.strokeRect(0,0,canvas.width, canvas.height);
and you will see, that your sprites have not the size you think of.
UPD. You can set the size of a canvas like this
var ctxFont = "bold " + fontsize + "px " + fontface;
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
context.font = ctxFont;
var metrics = context.measureText(message);
var textWidth = metrics.width;
canvas.width = textWidth + borderThickness * 2;
canvas.height = fontsize * 1.2 + (borderThickness * 2);
context = canvas.getContext('2d');
context.font = ctxFont;
and then set the scale of a sprite
sprite.scale.set(canvas.width / 10, canvas.height / 10, 1);
jsfiddle example
Your Three.js canvas probably isn't at the top left of the screen, and you're not taking into account the offset from 0,0 on the page. To fix it, adjust the mouse position to subtract the offset.
var rect = container.getBoundingClientRect();
mouse.x = ((event.clientX - rect.left) / renderer.domElement.clientWidth) * 2 - 1;
mouse.y = -((event.clientY - rect.top) / renderer.domElement.clientHeight) * 2 + 1;
I have a simple Pong game made with some Javascript and a Canvas tag.
I set the background color of the canvas tag to be transparent so the background image of the div tag can be displayed under the canvas.
The problem is that when I have it set to transparent it doesn't draw the ball and paddles correctly as if I set the background to a normal hex color. It draws the path of the paddles and the ball and the screen eventually turns the color of the ball.
The Javascript code is:
//canvas
var Width = 988;
var Height = 310;
var canvas = document.getElementById("Pong");
canvas.width = Width;
canvas.height = Height;
canvas.setAttribute('tabindex', 1);
var ctx = canvas.getContext("2d");
var FPS = 1000 / 60;
var BG = {
Color: 'transparent',
Paint: function(){
ctx.fillStyle = this.Color;
ctx.fillRect(0, 0, Width, Height);
}
};
//var Mouse = {X: 0, Y: 0};
var Ball = {
Radius: 6,
Color: '#ffffff',
X: 0,
Y: 0,
VelX: 0,
VelY: 0,
Paint: function(){
ctx.beginPath();
ctx.fillStyle = this.Color;
ctx.arc(this.X, this.Y, this.Radius, 0, Math.PI * 2, false);
ctx.fill();
this.Update();
},
Update: function(){
this.X += this.VelX;
this.Y += this.VelY;
},
Reset: function(){
this.X = Width/2;
this.Y = Height/2;
this.VelX = (!!Math.round(Math.random() * 1) ? 1.5 : -1.5);
this.VelY = (!!Math.round(Math.random() * 1) ? 1.5 : -1.5);
}
};
function Paddle(position){
this.Color = '#ffffff';
this.Width = 15;
this.Height = 60;
this.X = 0;
this.Y = Height/2 - this.Height/2;
this.Score = 0;
if(position == 'left')
this.X = 50;
else this.X = 938;
this.Paint = function(){
ctx.fillStyle = this.Color;
ctx.fillRect(this.X, this.Y, this.Width, this.Height);
ctx.fillStyle = this.Color;
ctx.font = "normal 10pt Calibri";
if(position == 'left'){
ctx.textAlign = "left";
ctx.fillText("score: " + Player.Score, 10, 10);
}else{
ctx.textAlign = "right";
ctx.fillText("score: " + Computer.Score, Width - 10, 10);
}
};
this.IsCollision = function () {
if (Ball.X - Ball.Radius > this.Width + this.X || this.X > Ball.Radius * 2 + Ball.X - Ball.Radius)
return false;
if (Ball.Y - Ball.Radius > this.Height + this.Y || this.Y > Ball.Radius * 2 + Ball.Y - Ball.Radius)
return false;
return true;
};
};
window.requestAnimFrame = (function(){
return window.requestAnimationFrame
|| window.webkitRequestAnimationFrame
|| window.mozRequestAnimationFrame
|| window.oRequestAnimationFrame
|| window.msRequestAnimationFrame
|| function( callback ){ return window.setTimeout(callback, FPS); }; }
)();
window.cancelRequestAnimFrame = (function() {
return window.cancelAnimationFrame
|| window.webkitCancelRequestAnimationFrame
|| window.mozCancelRequestAnimationFrame
|| window.oCancelRequestAnimationFrame
|| window.msCancelRequestAnimationFrame
|| clearTimeout }
)();
//game
var Computer = new Paddle();
var Player = new Paddle('left');
//event listener
function MouseMove(e){
Player.Y = e.pageY - Player.Height/2;
}
//attache event
canvas.addEventListener("mousemove", MouseMove, true);
function Paint(){
ctx.beginPath();
BG.Paint();
Computer.Paint();
Player.Paint();
Ball.Paint();
}
function Loop(){
init = requestAnimFrame(Loop);
Paint();
if(Player.IsCollision() || Computer.IsCollision()){
Ball.VelX = Ball.VelX * -1;
Ball.VelX += (Ball.VelX > 0 ? 0.5 : -0.5 );
if(Math.abs(Ball.VelX) > Ball.Radius * 1.5)
Ball.VelX = (Ball.VelX > 0 ? Ball.Radius * 1.5 : Ball.Radius * -1.5);
}
if(Ball.Y - Ball.Radius < 0 || Ball.Y + Ball.Radius > Height)
Ball.VelY = Ball.VelY * -1;
if(Ball.X - Ball.Radius <= 0){
Computer.Score++;
Ball.Reset();
}else if(Ball.X + Ball.Radius > Width){
Player.Score++;
Ball.Reset();
}
if(Computer.Score === 10)
GameOver(false);
else if(Player.Score === 10)
GameOver(true);
Computer.Y = (Computer.Y + Computer.Height/2 < Ball.Y ? Computer.Y + Computer.Vel : Computer.Y - Computer.Vel);
};
function GameOver(win){
cancelRequestAnimFrame(init);
BG.Paint();
ctx.fillStyle = "#ffffff";
ctx.font = "bold 40px Calibri";
ctx.textAlign = "center";
ctx.fillText((win ? "A WINNER IS YOU" : "GAME OVER"), Width/2, Height/2);
ctx.font = "normal 16px Calibri";
ctx.fillText("refresh to reply", Width/2, Height/2 + 20);
}
function NewGame(){
Ball.Reset();
Player.Score = 0;
Computer.Score = 0;
Computer.Vel = 1.25;
Loop();
}
NewGame();
I tried simply removing the BG var but it just did the same thing.
Update: I also tried with and without ctx.closePath with no success.
This is because you don't clear the canvas at the start of each frame. With a solid-colour background this doesn't matter, but with transparent you must do this:
ctx.clearRect(0,0,canvas.width,canvas.height);
You need to clear the canvas on every draw. Canvas keeps the content that has been drawn on it.
Having a transparent canvas isn't a good idea anyway. Transparency makes things slower as it has to be recomputed every time you draw anything to the canvas. Try drawing an image into it using the image draw functions. You might find that it can use the GPU to do the image composition.
I now have a customized Shape, and this shape is controlled by one global variable. Thus I assume I just need to change this global variable due to frame.time, erase the old shape, and create the new one.
But however it seems not working. The following is the simplified code.
<script>
var toControlShape;
var myDrawFunction(context) {
// toControlShape will be used here.
}
window.onload = function() {
var stage = new Kinetic.Stage({...});
var layer = new Kinetic.Layer();
var myShape = new Kinetic.Shape({
drawFunc: myDrawFunction,
...
});
layer.add(myShape);
stage.add(layer);
var animation = new Kinetic.Animation(function(frame) {
toControlShape = someFunction(frame.time);
myShape.remove();
myShape = new Kinetic.Shape({
drawFunc: myDrawFunction,
...
});
layer.add(myShape);
}, layer);
animation.start();
};
</script>
The shape displays properly as its initial state. But there is no animation.
I am pretty new to Javascript and HTML5. So there might be a lot of anti-patterns in this code. Pointing out them to me is also appreciated.
The complete code is here on jsFiddle
I think your doing it wrong. From the docs:
"Kinetic.Animation which modifies the shape's position with each
animation frame."
So i think you should stop remove the shape and instead just update the shape. Then it should probably the animation part work for you.
html ---
<!DOCTYPE html>
<html>
<head>
<title>Strath</title>
<meta http-equiv="X-UA-Compatible" content="IE=9" />
<script type="text/javascript" src="kinetic-v4.1.2.min.js"></script>
<script src="fiddle.js"></script>
<style type="text/css">
</style>
</head>
<body>
<div id="PureHTML5"></div>
</body>
</html>
js ---
var segsToSkip = 0;
var triangle;
window.onload = function() {
var stage = new Kinetic.Stage({
container: "PureHTML5",
width: 300,
height: 300
});
var layer = new Kinetic.Layer();
/*
* create a triangle shape by defining a
* drawing function which draws a triangle
*/
var box = new Kinetic.Shape({
drawFunc: myDrawFunction,
stroke: "white",
strokeWidth: 8
});
var bbox= new Kinetic.Rect({
x:10,
y:10,
width:10,
height:10,
fill: 'green'
});
triangle = new Kinetic.Shape({
drawFunc: function(context) {
context.beginPath();
context.moveTo(10, 10);
context.lineTo(20, 80);
context.quadraticCurveTo(30, 10, 26, 17);
context.closePath();
context.stroke();
},
fill: '#00D2FF',
stroke: 'black',
strokeWidth: 4
});
layer.add(bbox);
// add the triangle shape to the layer
layer.add(box);
layer.add(triangle);
// add the layer to the stage
stage.add(layer);
var animation = new Kinetic.Animation(function(frame) {
console.log(frame.time);
var newSegsToSkip = Math.round(frame.time / 200);
triangle.setX(newSegsToSkip);
triangle.setDrawFunc(function(context) {
context.beginPath();
context.moveTo(newSegsToSkip, 10);
context.lineTo(newSegsToSkip+20, 80);
context.quadraticCurveTo(30, 10, 26, 17);
context.closePath();
context.stroke();
});
}, layer);
var animation2 = new Kinetic.Animation(function(frame) {
var newSegsToSkip = Math.round(frame.time / 200);
if (newSegsToSkip == segsToSkip) return;
else {
segsToSkip = newSegsToSkip;
box.remove();
box = new Kinetic.Shape({
drawFunc: myDrawFunction,
stroke: "black",
strokeWidth: 8
});
layer.add(box);
}
}, layer);
animation.start();
animation2.start();
};
var myDrawFunction = function(context) {
var x = 50;
var y = 50;
var width = 200;
var height = 200;
var radius = 20;
var dashedLength = 6;
var segsToDraw = 58;
context.beginPath();
context.drawDashedBox(x, y, width, height, radius, dashedLength, segsToSkip, segsToDraw);
context.closePath();
// context.stroke();
//this.fill(context);
this.stroke(context);
}
var CP = window.CanvasRenderingContext2D && CanvasRenderingContext2D.prototype;
CP.drawDashedBox = function(x, y, width, height, radius, dashedLength, segsToSkip, segsToDraw) {
// init
var env = getStratEnvForRCBox(x, y, width, height, radius, dashedLength, segsToSkip, segsToDraw);
// 'right'->'upper-right'->'down'->'bottom-right'->'left'->'bottom-left'->'up'->'upper-left'->'right'->...
while (env.segsToDraw > 0) {
console.log('drawing direction: ', env.direction, 'segsToDraw:', env.segsToDraw);
env.putToConsole();
if (env.direction == 'right') {
env = drawDashedLineNew(env, x + width - radius, y + height, dashedLength, this);
env.direction = 'bottom-right';
}
else if (env.direction == 'upper-right') {
env = drawDashedArcNew(env, x + width - radius, y + radius, radius, 0, Math.PI / 2, dashedLength, this);
env.direction = 'left';
}
else if (env.direction == 'down') {
env = drawDashedLineNew(env, x, y + height - radius, dashedLength, this);
env.direction = 'bottom-left';
}
else if (env.direction == 'bottom-right') {
env = drawDashedArcNew(env, x + width - radius, y + height - radius, radius, 3 * Math.PI / 2, 2 * Math.PI, dashedLength, this);
env.direction = 'up';
}
else if (env.direction == 'left') {
env = drawDashedLineNew(env, x + radius, y, dashedLength, this);
env.direction = 'upper-left';
}
else if (env.direction == 'bottom-left') {
env = drawDashedArcNew(env, x + radius, y + height - radius, radius, Math.PI, 3 * Math.PI / 2, dashedLength, this);
env.direction = 'right';
}
else if (env.direction == 'up') {
env = drawDashedLineNew(env, x + width, y + radius, dashedLength, this);
env.direction = 'upper-right';
}
else if (env.direction == 'upper-left') {
env = drawDashedArcNew(env, x + radius, y + radius, radius, Math.PI / 2, Math.PI, dashedLength, this);
env.direction = 'down';
}
}
}
function getStratEnvForRCBox(x, y, width, height, radius, dashLength, segsToSkip, segsToDraw) {
var direction = 'right';
var startX, startY;
if (direction == 'down') {
startX = x; startY = y + radius;
} else if (direction == 'right') {
startX = x + radius; startY = y + height;
} else if (direction == 'up') {
startX = x + width; startY = y + height - radius;
} else if (direction == 'left') {
startX = x + width - radius; startY = y;
}
var env = new Environment(startX, startY, 'gap', 0, direction, segsToSkip, segsToDraw);
return env;
}
function drawDashedLineNew(env, endX, endY, dashedLength, context) {
var dx = (endX - env.x), dy = (endY - env.y);
var angle = Math.atan2(dy, dx);
console.log('drawing line: angle =', angle, ' , ', env.gapOrDash, ' =', env.remainingLengthFromLastDraw);
var fromX = env.x, fromY = env.y;
// deal with remining
// we start loop from a fresh dash
if (env.gapOrDash == 'dash') {
// check if we need to skip
if (env.segsToSkip > 0) {
env.segsToSkip --;
} else {
context.moveTo(env.x, env.y);
context.lineTo(env.x + env.remainingLengthFromLastDraw * Math.cos(angle), env.y + env.remainingLengthFromLastDraw * Math.sin(angle));
// check if we quit
env.segsToDraw --;
if (env.segsToDraw == 0) return env;
}
// a full gap
fromX = env.x + (env.remainingLengthFromLastDraw + dashedLength) * Math.cos(angle);
fromY = env.y + (env.remainingLengthFromLastDraw + dashedLength) * Math.sin(angle);
} else if (env.gapOrDash == 'gap') {
fromX = env.x + env.remainingLengthFromLastDraw * Math.cos(angle);
fromY = env.y + env.remainingLengthFromLastDraw * Math.sin(angle);
}
var length = (endX - fromX) / Math.cos(angle);
if (endX - fromX == 0) length = Math.abs(endY - fromY);
var n = length / dashedLength;
var draw = true;
var x = fromX, y = fromY;
context.moveTo(x, y);
for (var i = 0; i < n; i++) {
x += dashedLength * Math.cos(angle);
y += dashedLength * Math.sin(angle);
if (draw) {
// check if we need to skip
if (env.segsToSkip > 0) {
env.segsToSkip --;
} else {
context.lineTo(x,y);
// check if we quit
env.segsToDraw --;
if (env.segsToDraw == 0) return env;
}
} else context.moveTo(x, y);
draw = !draw;
}
// deal with remaining
if (draw) {
// check if we need to skip
if (env.segsToSkip > 0) {
env.segsToSkip --;
} else
context.lineTo(endX, endY);
}
env.x = endX;
env.y = endY;
draw ? env.gapOrDash = 'dash' : env.gapOrDash = 'gap';
env.remainingLengthFromLastDraw = dashedLength - (endX - x) / Math.cos(angle);
return env;
}
function drawDashedArcNew(env, x, y, radius, startAngle, endAngle, dashedLength, context) {
var points = [];
var n = radius * Math.PI * 2/ dashedLength;
var stepAngle = Math.PI * 2 / n;
// deal with remaining
var angle = Math.asin(env.remainingLengthFromLastDraw / 2 / radius) * 2;
if (env.gapOrDash == 'dash') {
var angle = Math.asin(env.remainingLengthFromLastDraw / 2 / radius) * 2;
points.push({
x : (Math.cos(startAngle) * radius) + x,
y : - (Math.sin(startAngle) * radius) + y,
ex : (Math.cos(startAngle + angle) * radius) + x,
ey : - (Math.sin(startAngle + angle) * radius) + y
});
startAngle += stepAngle + angle;
} else {
startAngle += angle;
}
var draw = true;
while(startAngle + stepAngle <= endAngle) {
if (draw) {
points.push({
x : (Math.cos(startAngle) * radius) + x,
y : - (Math.sin(startAngle) * radius) + y,
ex : (Math.cos(startAngle + stepAngle) * radius) + x,
ey : - (Math.sin(startAngle + stepAngle) * radius) + y
});
}
startAngle += stepAngle;
draw = !draw;
}
// deal with the remaining
var endX = (Math.cos(endAngle) * radius) + x;
var endY = - (Math.sin(endAngle) * radius) + y;
//console.log('drawing arc: end-x:', endX, ',end-y:', endY);
if (draw) {
points.push({
x : (Math.cos(startAngle) * radius) + x,
y : - (Math.sin(startAngle) * radius) + y,
ex : endX,
ey : endY
});
}
env.x = endX;
env.y = endY;
draw ? env.gapOrDash = 'dash' : env.gapOrDash = 'gap';
env.remainingLengthFromLastDraw = dashedLength - radius * Math.sin( (endAngle - startAngle) / 2) * 2;
for(p = 0; p < points.length; p++){
//console.log('draw arc seg: from(', points[p].x, ',', points[p].y, ') to (', points[p].ex, ',', points[p].ey, ')');
// check if we need to skip
if (env.segsToSkip > 0) {
env.segsToSkip --;
} else {
context.moveTo(points[p].x, points[p].y);
context.lineTo(points[p].ex, points[p].ey);
// check if we quit
env.segsToDraw --;
if (env.segsToDraw == 0) return env;
}
}
return env;
}
function Environment(x, y, gapOrDash, remainingLengthFromLastDraw, direction, segsToSkip, segsToDraw) {
this.x = x;
this.y = y;
this.gapOrDash = gapOrDash;
this.remainingLengthFromLastDraw = remainingLengthFromLastDraw;
this.direction = direction;
this.segsToSkip = segsToSkip;
this.segsToDraw = segsToDraw;
}
Environment.prototype.putToConsole = function() {
//console.log('Environment:');
//console.log('x:', this.x, ',y:', this.y, 'direction:', this.direction);
//console.log('toSkip:', this.segsToSkip, 'toDraw:', this.segsToDraw);
}