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}56.wrapper {7 position: absolute;8 top: 50%;9 left: 50%;10 transform: translate(-50%, -50%);11}1213@keyframes round {14 from {15 transform: rotate(0deg);16 }1718 to {19 transform: rotate(360deg);20 }21}2223.box {24 width: 200px;25 height: 200px;26 border: 1px solid blue;27 border-radius: 50%;28 animation: round 4s linear infinite;29}303132.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 }1415 .container {16 position: relative;17 width: 420px;18 height: 420px;19 border-radius: 210px;20 margin: 200px auto;21 }2223 .panel {24 position: absolute;25 bottom: -200px;26 left: 0;27 right: 0;28 text-align: center;29 }3031 .ball {32 position: absolute;33 width: 20px;34 height: 20px;35 border-radius: 10px;36 background: red;37 }3839 .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>4849 <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>5657 <p>58 <button class="add">添加</button> <button class="del">删除</button>59 </p>60 </div>61 </div>6263 <script src="./ball.js"></script>64 <scirpt src="./ballGroup.js"></scirpt>65 <script>66 window.onload = function() {67 // buttons68 var runBtn = document.querySelector('.run');69 var pauseBtn = document.querySelector('.pause');70 var addBtn = document.querySelector('.add');71 var delBtn = document.querySelector('.del');7273 // 容器74 var container = document.querySelector('.container');7576 // 球形组实例77 var group = new BallGroup(container, 30, 200, 200);7879 // 需要注意回调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;1516 var fragment = document.createDocumentFragment();1718 // 插入圆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);2728 // 插入小球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);3435 this.container.appendChild(fragment);36 // 角度矫正37 this.run.call(this, this.n);38};3940// 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};5253/**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};1819// 修改了Ball类与BallGroup类的run方法。20// 将属于一个group的所有小球的run方法放到一个requestAnimationFrame的回调中执行。21// 而不是单独将每个小球的run方法加入一个requestAnimationFrame中。22// 试图改善性能,然而貌似并没什么卵用。23BallGroup.prototype.run = function() {24 if (this.group.length === 0 || this.timer) return;2526 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 }3334 _run.call(this);35};3637BallGroup.prototype.pause = function() {38 if (this.timer) {39 window.cancelAnimationFrame(this.timer);40 this.timer = null;41 }42};4344BallGroup.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.n52 );53 this.uid++;54 this.group.push(ball);55};5657BallGroup.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};