<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>链式弹动</title>
</head>
<body>
<canvas id="canvas" width="400" height="400"></canvas>
<p id="text">Bolero</p>
<script>
//ball.js
function Ball(radius, color) {
if(radius === undefined) {
radius = 40;
}
if(color === undefined) {
color = "#ff0000";
}
this.x = 0;
this.y = 0;
this.radius = radius;
this.vx = 0;
this.vy = 0;
this.rotation = 0;
this.scaleX = 1;
this.scaleY = 1;
this.color = utils.parseColor(color);
this.lineWidth = 1;
}
Ball.prototype.draw = function(context) {
context.save();
context.translate(this.x, this.y);
context.rotate(this.rotation);
context.scale(this.scaleX, this.scaleY);
context.lineWidth = this.lineWidth;
context.fillStyle = this.color;
context.beginPath();
context.arc(0, 0, this.radius, 0, (Math.PI * 2), true)
context.closePath();
context.fill();
if(this.lineWidth > 0) {
context.stroke();
}
context.restore();
};
Ball.prototype.getBounds = function() {
return {
x: this.x - this.radius,
y: this.y - this.radius,
width: this.radius * 2,
height: this.radius * 2
};
};
//utils.js
if(!window.requestAnimationFrame) {
window.requestAnimationFrame = (window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.msRequestAnimationFrame ||
window.oRequestAnimationFrame ||
function(callback) {
return window.setTimeout(callback, 17);
});
}
if(!window.cancelRequestAnimationFrame) {
window.cancelRequestAnimationFrame = (window.cancelAnimationFrame ||
window.webkitCancelRequestAnimationFrame ||
window.mozCancelRequestAnimationFrame ||
window.msCancelRequestAnimationFrame ||
window.oCancelRequestAnimationFrame ||
window.clearTimeout);
}
window.utils = {};
window.utils.captureMouse = function(element) {
var mouse = {
x: 0,
y: 0,
event: null
},
body_scrollLeft = document.body.scrollLeft,
element_scrollLeft = document.documentElement.scrollLeft,
body_scrollTop = document.body.scrollTop,
element_scrollTop = document.documentElement.scrollTop,
offsetLeft = element.offsetLeft,
offsetTop = element.offsetTop;
element.addEventListener('mousemove', function(event) {
var x, y;
if(event.pageX || event.pageY) {
x = event.pageX;
y = event.pageY;
} else {
x = event.clientX + body_scrollLeft + element_scrollLeft;
y = event.clientY + body_scrollTop + element_scrollTop;
}
x -= offsetLeft;
y -= offsetTop;
mouse.x = x;
mouse.y = y;
mouse.event = event;
}, false);
return mouse;
};
window.utils.captureTouch = function(element) {
var touch = {
x: null,
y: null,
isPressed: false,
event: null
},
body_scrollLeft = document.body.scrollLeft,
element_scrollLeft = document.documentElement.scrollLeft,
body_scrollTop = document.body.scrollTop,
element_scrollTop = document.documentElement.scrollTop,
offsetLeft = element.offsetLeft,
offsetTop = element.offsetTop;
element.addEventListener('touchstart', function(event) {
touch.isPressed = true;
touch.event = event;
}, false);
element.addEventListener('touchend', function(event) {
touch.isPressed = false;
touch.x = null;
touch.y = null;
touch.event = event;
}, false);
element.addEventListener('touchmove', function(event) {
var x, y,
touch_event = event.touches[0];
if(touch_event.pageX || touch_event.pageY) {
x = touch_event.pageX;
y = touch_event.pageY;
} else {
x = touch_event.clientX + body_scrollLeft + element_scrollLeft;
y = touch_event.clientY + body_scrollTop + element_scrollTop;
}
x -= offsetLeft;
y -= offsetTop;
touch.x = x;
touch.y = y;
touch.event = event;
}, false);
return touch;
};
window.utils.parseColor = function(color, toNumber) {
if(toNumber === true) {
if(typeof color === 'number') {
return(color | 0);
}
if(typeof color === 'string' && color[0] === '#') {
color = color.slice(1);
}
return window.parseInt(color, 16);
} else {
if(typeof color === 'number') {
color = '#' + ('00000' + (color | 0).toString(16)).substr(-6); //pad
}
return color;
}
};
window.utils.colorToRGB = function(color, alpha) {
if(typeof color === 'string' && color[0] === '#') {
color = window.parseInt(color.slice(1), 16);
}
alpha = (alpha === undefined) ? 1 : alpha;
var r = color >> 16 & 0xff,
g = color >> 8 & 0xff,
b = color & 0xff,
a = (alpha < 0) ? 0 : ((alpha > 1) ? 1 : alpha);
if(a === 1) {
return "rgb(" + r + "," + g + "," + b + ")";
} else {
return "rgba(" + r + "," + g + "," + b + "," + a + ")";
}
};
window.utils.containsPoint = function(rect, x, y) {
return !(x < rect.x ||
x > rect.x + rect.width ||
y < rect.y ||
y > rect.y + rect.height);
};
window.utils.intersects = function(rectA, rectB) {
return !(rectA.x + rectA.width < rectB.x ||
rectB.x + rectB.width < rectA.x ||
rectA.y + rectA.height < rectB.y ||
rectB.y + rectB.height < rectA.y);
};
//index.html
window.onload = function() {
var canvas = document.getElementById('canvas'),
context = canvas.getContext('2d'),
mouse = utils.captureMouse(canvas),
balls = [],
numBalls = 6,
spring = 0.03,
friction = 0.8,
gravity = 1;
while(numBalls--) {
balls.push(new Ball(5));
}
function move(ball, targetX, targetY) {
ball.vx += (targetX - ball.x) * spring;
ball.vy += (targetY - ball.y) * spring;
ball.vx += gravity;
ball.vx *= friction;
ball.vy *= friction;
ball.x += ball.vx;
ball.y += ball.vy;
}
function draw(ballB, i) {
if(i === 0) {
move(ballB, mouse.x - 25, mouse.y);
context.moveTo(mouse.x, mouse.y);
} else {
var ballA = balls[i - 1];
move(ballB, ballA.x, ballA.y);
context.moveTo(ballA.x, ballA.y);
}
ballB.draw(context);
}
(function drawFrame() {
window.requestAnimationFrame(drawFrame, canvas);
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
balls.forEach(draw);
}());
};
</script>
</body>
</html>
<html>
<head>
<meta charset="utf-8">
<title>链式弹动</title>
</head>
<body>
<canvas id="canvas" width="400" height="400"></canvas>
<p id="text">Bolero</p>
<script>
//ball.js
function Ball(radius, color) {
if(radius === undefined) {
radius = 40;
}
if(color === undefined) {
color = "#ff0000";
}
this.x = 0;
this.y = 0;
this.radius = radius;
this.vx = 0;
this.vy = 0;
this.rotation = 0;
this.scaleX = 1;
this.scaleY = 1;
this.color = utils.parseColor(color);
this.lineWidth = 1;
}
Ball.prototype.draw = function(context) {
context.save();
context.translate(this.x, this.y);
context.rotate(this.rotation);
context.scale(this.scaleX, this.scaleY);
context.lineWidth = this.lineWidth;
context.fillStyle = this.color;
context.beginPath();
context.arc(0, 0, this.radius, 0, (Math.PI * 2), true)
context.closePath();
context.fill();
if(this.lineWidth > 0) {
context.stroke();
}
context.restore();
};
Ball.prototype.getBounds = function() {
return {
x: this.x - this.radius,
y: this.y - this.radius,
width: this.radius * 2,
height: this.radius * 2
};
};
//utils.js
if(!window.requestAnimationFrame) {
window.requestAnimationFrame = (window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.msRequestAnimationFrame ||
window.oRequestAnimationFrame ||
function(callback) {
return window.setTimeout(callback, 17);
});
}
if(!window.cancelRequestAnimationFrame) {
window.cancelRequestAnimationFrame = (window.cancelAnimationFrame ||
window.webkitCancelRequestAnimationFrame ||
window.mozCancelRequestAnimationFrame ||
window.msCancelRequestAnimationFrame ||
window.oCancelRequestAnimationFrame ||
window.clearTimeout);
}
window.utils = {};
window.utils.captureMouse = function(element) {
var mouse = {
x: 0,
y: 0,
event: null
},
body_scrollLeft = document.body.scrollLeft,
element_scrollLeft = document.documentElement.scrollLeft,
body_scrollTop = document.body.scrollTop,
element_scrollTop = document.documentElement.scrollTop,
offsetLeft = element.offsetLeft,
offsetTop = element.offsetTop;
element.addEventListener('mousemove', function(event) {
var x, y;
if(event.pageX || event.pageY) {
x = event.pageX;
y = event.pageY;
} else {
x = event.clientX + body_scrollLeft + element_scrollLeft;
y = event.clientY + body_scrollTop + element_scrollTop;
}
x -= offsetLeft;
y -= offsetTop;
mouse.x = x;
mouse.y = y;
mouse.event = event;
}, false);
return mouse;
};
window.utils.captureTouch = function(element) {
var touch = {
x: null,
y: null,
isPressed: false,
event: null
},
body_scrollLeft = document.body.scrollLeft,
element_scrollLeft = document.documentElement.scrollLeft,
body_scrollTop = document.body.scrollTop,
element_scrollTop = document.documentElement.scrollTop,
offsetLeft = element.offsetLeft,
offsetTop = element.offsetTop;
element.addEventListener('touchstart', function(event) {
touch.isPressed = true;
touch.event = event;
}, false);
element.addEventListener('touchend', function(event) {
touch.isPressed = false;
touch.x = null;
touch.y = null;
touch.event = event;
}, false);
element.addEventListener('touchmove', function(event) {
var x, y,
touch_event = event.touches[0];
if(touch_event.pageX || touch_event.pageY) {
x = touch_event.pageX;
y = touch_event.pageY;
} else {
x = touch_event.clientX + body_scrollLeft + element_scrollLeft;
y = touch_event.clientY + body_scrollTop + element_scrollTop;
}
x -= offsetLeft;
y -= offsetTop;
touch.x = x;
touch.y = y;
touch.event = event;
}, false);
return touch;
};
window.utils.parseColor = function(color, toNumber) {
if(toNumber === true) {
if(typeof color === 'number') {
return(color | 0);
}
if(typeof color === 'string' && color[0] === '#') {
color = color.slice(1);
}
return window.parseInt(color, 16);
} else {
if(typeof color === 'number') {
color = '#' + ('00000' + (color | 0).toString(16)).substr(-6); //pad
}
return color;
}
};
window.utils.colorToRGB = function(color, alpha) {
if(typeof color === 'string' && color[0] === '#') {
color = window.parseInt(color.slice(1), 16);
}
alpha = (alpha === undefined) ? 1 : alpha;
var r = color >> 16 & 0xff,
g = color >> 8 & 0xff,
b = color & 0xff,
a = (alpha < 0) ? 0 : ((alpha > 1) ? 1 : alpha);
if(a === 1) {
return "rgb(" + r + "," + g + "," + b + ")";
} else {
return "rgba(" + r + "," + g + "," + b + "," + a + ")";
}
};
window.utils.containsPoint = function(rect, x, y) {
return !(x < rect.x ||
x > rect.x + rect.width ||
y < rect.y ||
y > rect.y + rect.height);
};
window.utils.intersects = function(rectA, rectB) {
return !(rectA.x + rectA.width < rectB.x ||
rectB.x + rectB.width < rectA.x ||
rectA.y + rectA.height < rectB.y ||
rectB.y + rectB.height < rectA.y);
};
//index.html
window.onload = function() {
var canvas = document.getElementById('canvas'),
context = canvas.getContext('2d'),
mouse = utils.captureMouse(canvas),
balls = [],
numBalls = 6,
spring = 0.03,
friction = 0.8,
gravity = 1;
while(numBalls--) {
balls.push(new Ball(5));
}
function move(ball, targetX, targetY) {
ball.vx += (targetX - ball.x) * spring;
ball.vy += (targetY - ball.y) * spring;
ball.vx += gravity;
ball.vx *= friction;
ball.vy *= friction;
ball.x += ball.vx;
ball.y += ball.vy;
}
function draw(ballB, i) {
if(i === 0) {
move(ballB, mouse.x - 25, mouse.y);
context.moveTo(mouse.x, mouse.y);
} else {
var ballA = balls[i - 1];
move(ballB, ballA.x, ballA.y);
context.moveTo(ballA.x, ballA.y);
}
ballB.draw(context);
}
(function drawFrame() {
window.requestAnimationFrame(drawFrame, canvas);
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
balls.forEach(draw);
}());
};
</script>
</body>
</html>