Permalink
Fetching contributors…
Cannot retrieve contributors at this time
220 lines (192 sloc) 6.06 KB
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Bobbing 1</title>
<link rel="stylesheet" href="../include/style.css">
<style type="text/css">
#canvas {
background: #fff;
}
</style>
</head>
<body>
<header>
Example from <a href="http://amzn.com/1430236655?tag=html5anim-20"><em>Foundation HTML5 Animation with JavaScript</em></a>
</header>
<canvas id="canvas" width="640" height="340"></canvas>
<script type="module">
import {captureMouse, circleContainsPoint, parseColor} from '../include/utils.js';
import Ball from './classes/ball-with-text.js';
window.onload = function () {
const canvas = document.getElementById('canvas'),
context = canvas.getContext('2d'),
{width, height} = canvas,
mouse = captureMouse(canvas),
spring = 0.03,
friction = 0.9,
offset = 50;
let balls = [];
let tempBalls = [];
let ballNum = 8;
let isDragingBall = false;
while(ballNum--) {
let ball = new Ball(10, parseColor(Math.random() * 0xffffff))
ball.x = Math.random() * width;
ball.y = Math.random() * height;
tempBalls.push(ball)
}
// 让点按X轴升序排序
tempBalls = tempBalls.sort((ballA, ballB) => {
return ballA.x - ballB.x
})
// 找X轴左右极点
let firstBall = tempBalls[0],
lastBall = tempBalls[tempBalls.length -1];
let smallXBalls = tempBalls.filter(ball => ball.x === firstBall.x),
bigXBalls = tempBalls.filter(ball => ball.x === lastBall.x)
// 处理左右极点有多个的情况
if (smallXBalls.length > 1) {
smallXBalls.sort((ballA, ballB) => {
return ballB.y - ballA.y
})
}
if (bigXBalls.length > 1) {
bigXBalls.sort((ballA, ballB) => {
return ballB.y - ballA.y
})
}
firstBall = smallXBalls[0]
lastBall = bigXBalls[0]
// 获得极点连线的角度
let splitLineAngle = Math.atan2(lastBall.y - firstBall.y, lastBall.x - firstBall.x);
let upperBalls = [],
lowerBalls = [];
// 所有其他点跟firstBall计算角度
// 大于splitLineAngle的都是下链
// 其他是上链
tempBalls.forEach(ball => {
if (ball === firstBall || ball === lastBall) {
return false
}
let angle = Math.atan2(ball.y - firstBall.y, ball.x - firstBall.x);
if (angle > splitLineAngle) {
lowerBalls.push(ball)
} else {
upperBalls.push(ball)
}
})
// 处理X轴相同情况的排序
lowerBalls = lowerBalls.sort((ballA, ballB) => {
if (ballA.x !== ballB.x) {
return ballA.x - ballB.x
}
return ballB.y - ballA.y
})
upperBalls = upperBalls.sort((ballA, ballB) => {
if (ballA.x !== ballB.x) {
return ballB.x - ballA.x
}
return ballB.y - ballB.x
})
// 逆时针连接所有的点
balls = [firstBall].concat(lowerBalls, [lastBall], upperBalls)
balls = balls.map((ball, i) => {
ball.text = i + 1;
return ball
})
console.log(balls)
console.log({
firstBall: firstBall.text,
lastBall: lastBall.text,
upperBalls: upperBalls.map(ball => ball.text),
lowerBalls: lowerBalls.map(ball => ball.text)
})
function getCenterPoint(ball1, ball2) {
return {
x: (ball1.x + ball2.x) / 2,
y: (ball1.y + ball2.y) / 2
}
}
function getRealTarget(ball, mouse, offset) {
const dx = ball.x - mouse.x,
dy = ball.y - mouse.y,
angle = Math.atan2(dy, dx);
return {
x: mouse.x + Math.cos(angle) * offset,
y: mouse.y + Math.sin(angle) * offset
};
}
// 拖拽球
function dragBall(ball) {
if (ball.isDrag) {
ball.x = mouse.x;
ball.y = mouse.y;
}
}
// 应用弹力
function applySpring(ball, centerPoint) {
const target = getRealTarget(ball, centerPoint, offset);
ball.vx += (target.x - ball.x) * spring;
ball.vy += (target.y - ball.y) * spring;
}
// 最终移动球
function moveBall(ball) {
ball.vx *= friction;
ball.vy *= friction;
ball.x += ball.vx;
ball.y += ball.vy;
}
// 遍历所有的球,设定球之间的作用力
function setAllBalls() {
for(let i = 0, len = balls.length; i < len; i++) {
let nextIndex = (i + 1) % len;
let ballA = balls[i],
ballB = balls[nextIndex];
const centerPoint = getCenterPoint(ballA, ballB)
if (!ballA.isDrag) {
applySpring(ballA, centerPoint)
}
if (!ballB.isDrag) {
applySpring(ballB, centerPoint)
}
}
}
function drawLines() {
context.beginPath();
context.moveTo(balls[0].x, balls[0].y);
for (let i = 1, len = balls.length; i <= len; i++) {
let index = i % len;
context.lineTo(balls[index].x, balls[index].y);
}
context.stroke();
}
canvas.addEventListener('mousedown', () => {
balls.forEach(ball => {
if (circleContainsPoint(ball, mouse)) {
if (!isDragingBall) {
ball.isDrag = true
isDragingBall = true
}
}
})
}, false);
canvas.addEventListener('mouseup', () => {
balls.forEach(ball => {
ball.isDrag = false
})
isDragingBall = false
}, false)
;(function draw(){
window.requestAnimationFrame(draw);
context.clearRect(0, 0, width, height);
balls.forEach(dragBall);
setAllBalls();
balls.forEach(ball => moveBall(ball));
drawLines()
balls.forEach(ball => ball.draw(context));
}());
}
</script>
</body>
</html>