实现圆周运动

Li Hao
June 11th, 2018 · 1 min read

CSS的方式

html

1<div class="wrapper">
2 <div class="box">
3 <div class="ball"></div>
4 </div>
5</div>

css

1html, body {
2 margin: 0;
3 padding: 0;
4}
5
6.wrapper {
7 position: absolute;
8 top: 50%;
9 left: 50%;
10 transform: translate(-50%, -50%);
11}
12
13@keyframes round {
14 from {
15 transform: rotate(0deg);
16 }
17
18 to {
19 transform: rotate(360deg);
20 }
21}
22
23.box {
24 width: 200px;
25 height: 200px;
26 border: 1px solid blue;
27 border-radius: 50%;
28 animation: round 4s linear infinite;
29}
30
31
32.ball {
33 position: absolute;
34 width: 10px;
35 height: 10px;
36 border-radius: 50%;
37 background: red;
38 left: 50%;
39 transform: translate(-50%, -50%);
40}

JS的方式

圆周运动动画

index.html

1<!DOCTYPE html>
2<html lang="en">
3 <head>
4 <meta charset="UTF-8" />
5 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6 <meta http-equiv="X-UA-Compatible" content="ie=edge" />
7 <title>Ball</title>
8 <style>
9 html,
10 body {
11 padding: 0;
12 margin: 0;
13 }
14
15 .container {
16 position: relative;
17 width: 420px;
18 height: 420px;
19 border-radius: 210px;
20 margin: 200px auto;
21 }
22
23 .panel {
24 position: absolute;
25 bottom: -200px;
26 left: 0;
27 right: 0;
28 text-align: center;
29 }
30
31 .ball {
32 position: absolute;
33 width: 20px;
34 height: 20px;
35 border-radius: 10px;
36 background: red;
37 }
38
39 .circle {
40 box-sizing: border-box;
41 position: absolute;
42 border: 1px solid#1E90FF;
43 border-radius: 250px;
44 background: transparent;
45 }
46 </style>
47 </head>
48
49 <body>
50 <div class="container">
51 <div class="panel">
52 <p>点击“添加”,生成小球后运行。</p>
53 <p>
54 <button class="run">启动</button> <button class="pause">暂停</button>
55 </p>
56
57 <p>
58 <button class="add">添加</button> <button class="del">删除</button>
59 </p>
60 </div>
61 </div>
62
63 <script src="./ball.js"></script>
64 <scirpt src="./ballGroup.js"></scirpt>
65 <script>
66 window.onload = function() {
67 // buttons
68 var runBtn = document.querySelector('.run');
69 var pauseBtn = document.querySelector('.pause');
70 var addBtn = document.querySelector('.add');
71 var delBtn = document.querySelector('.del');
72
73 // 容器
74 var container = document.querySelector('.container');
75
76 // 球形组实例
77 var group = new BallGroup(container, 30, 200, 200);
78
79 // 需要注意回调this指向:不使用bind,this指向window。
80 runBtn.addEventListener('click', group.run.bind(group));
81 pauseBtn.addEventListener('click', group.pause.bind(group));
82 addBtn.addEventListener('click', group.add.bind(group));
83 delBtn.addEventListener('click', group.remove.bind(group));
84 };
85 </script>
86 </body>
87</html>

ball.js

1/**
2 * @description 小球类(在container中插入小球与圆形轨道)
3 * @param r {number} 半径
4 * @param x {number} 圆心X轴坐标
5 * @param y {number} 圆心y轴坐标
6 * @param n {number} 小球初始角度
7 * @param container {DOMElement} 容器DOM元素
8 */
9var Ball = function(container, r, x, y, n) {
10 this.container = container;
11 this.r = r;
12 this.x = x || 500;
13 this.y = y || 500;
14 this.n = n || 0;
15
16 var fragment = document.createDocumentFragment();
17
18 // 插入圆
19 var style = window.getComputedStyle(this.container);
20 this.circle = document.createElement('div');
21 this.circle.classList.add('circle');
22 this.circle.style.width = 2 * r + 'px';
23 this.circle.style.height = 2 * r + 'px';
24 this.circle.style.top = parseInt(style.height) / 2 - r + 'px';
25 this.circle.style.left = parseInt(style.width) / 2 - r + 'px';
26 fragment.appendChild(this.circle);
27
28 // 插入小球
29 this.el = document.createElement('div');
30 this.el.classList.add('ball');
31 this.el.style.top = y - r + 'px';
32 this.el.style.left = x + 'px';
33 fragment.appendChild(this.el);
34
35 this.container.appendChild(fragment);
36 // 角度矫正
37 this.run.call(this, this.n);
38};
39
40// TODO: 优化动画性能
41/**
42 * @description 小球类run方法(旋转到角度n)
43 * @param n {number} 小球目标角度
44 */
45Ball.prototype.run = function(n) {
46 this.n = n;
47 var a = Math.cos((this.n * Math.PI) / 180) * this.r;
48 var b = Math.sin((this.n * Math.PI) / 180) * this.r;
49 this.el.style.left = this.x + a + 'px';
50 this.el.style.top = this.y + b + 'px';
51};
52
53/**
54 * @description 移除小球与圆形轨道
55 */
56Ball.prototype.remove = function() {
57 this.container.removeChild(this.el);
58 this.container.removeChild(this.circle);
59};

ballGroup.js

1/**
2 * @description 小球组类
3 * @param container {DOMElement} 容器DOM元素
4 * @param interval {number} 小球运行轨道半径间隔
5 * @param x {number} 圆心X轴坐标
6 * @param y {number} 圆心y轴坐标
7 */
8var BallGroup = function(container, interval, x, y) {
9 this.group = [];
10 this.n = 0;
11 this.uid = 0;
12 this.running = false;
13 this.x = x;
14 this.y = y;
15 this.interval = interval;
16 this.container = container || document.querySelector('body');
17};
18
19// 修改了Ball类与BallGroup类的run方法。
20// 将属于一个group的所有小球的run方法放到一个requestAnimationFrame的回调中执行。
21// 而不是单独将每个小球的run方法加入一个requestAnimationFrame中。
22// 试图改善性能,然而貌似并没什么卵用。
23BallGroup.prototype.run = function() {
24 if (this.group.length === 0 || this.timer) return;
25
26 function _run() {
27 this.n = ++this.n % 360;
28 this.group.forEach(function(ball) {
29 ball.run(this.n);
30 }, this);
31 this.timer = window.requestAnimationFrame(_run.bind(this));
32 }
33
34 _run.call(this);
35};
36
37BallGroup.prototype.pause = function() {
38 if (this.timer) {
39 window.cancelAnimationFrame(this.timer);
40 this.timer = null;
41 }
42};
43
44BallGroup.prototype.add = function() {
45 if (this.uid > 8) return;
46 var ball = new Ball(
47 this.container,
48 this.uid * this.interval,
49 this.x,
50 this.y,
51 this.n
52 );
53 this.uid++;
54 this.group.push(ball);
55};
56
57BallGroup.prototype.remove = function() {
58 if (this.group.length > 0) {
59 this.uid--;
60 this.group.pop().remove();
61 if (this.group.length === 0) {
62 this.pause();
63 }
64 }
65};

More articles from Li Hao