针对https://github.com/bitjjj/JS-3D-TagCloud这个版本的做了移动端性能优化(使用transform做偏移及缩放,优化帧)。基本原理一致。
class TagCould { mcList = []; active = false; // 事件控制 lasta = 1; lastb = 1; distr = true; mouseX = 0; mouseY = 0; aA = null; oDiv = null;
_now = 0; _then = Date.now(); _delta = 0;
isStart = false; defaultOptions = { dtr: Math.PI / 180, d: 500, tspeed: 5, size: 250, howElliptical: 1, fps: 30, radius: (window.innerWidth + 25) / 2 > 300 ? 300 : (window.innerWidth + 25) / 2 }; constructor(container, tags = [], options = {}) { this.container = container; this.tags = tags; options = Object.assign(this.defaultOptions, options); for (var p in options) { this[p] = options[p]; } this._interval = 1000 / this.fps; window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
this.init(); }
init() { this.createTag(); this.setOffset(); this.sineCosine(0, 0, 0); this.positionAll(); this.tick(); this.bindEvent(); } start() { this.isStart = true; } pause() { this.isStart = false; } createTag() { this.oDiv = typeof this.container == 'string' ? document.getElementById(this.container) : this.container; for (let i = 0; i < this.tags.length; i++) { const item = this.tags[i]; let aElE = document.createElement('a'); aElE.innerHTML = item.text; aElE.classList.add(`tag${i}`); aElE.classList.add(`tag`); item.className && aElE.classList.add(item.className); aElE.setAttribute('href', item.url || 'javascript:;'); this.oDiv.appendChild(aElE); } } setOffset() { this.oDiv = typeof this.container == 'string' ? document.getElementById(this.container) : this.container; let i = 0, oTag = null; this.aA = this.oDiv.getElementsByTagName('a'); for (i = 0; i < this.aA.length; i++) { oTag = {}; oTag.offsetWidth = this.aA[i].offsetWidth; oTag.offsetHeight = this.aA[i].offsetHeight; this.mcList.push(oTag); } } bindEvent() { let self = this; document.addEventListener( 'mouseover', function() { self.active = true; }, false );
document.addEventListener( 'mouseout', function() { self.active = false; }, false );
document.addEventListener( 'mousemove', function(evt) { //var oEvent=window.event || evt; self.onmove(window.event || evt); }, false );
document.addEventListener( 'touchstart', function() { self.active = true; }, false ); document.addEventListener( 'touchmove', function(evt) { self.onmove(window.event || evt); }, false ); document.addEventListener( 'touchend', function() { self.active = false; }, false ); } tick() { if (window.requestAnimationFrame) { window.requestAnimationFrame(this.tick.bind(this)); this._now = Date.now(); this._delta = this._now - this._then; if (this._delta > this._interval) { // 这里不能简单then=now,否则还会出现上边简单做法的细微时间差问题。例如fps=10,每帧100ms,而现在每16ms(60fps)执行一次draw。16*7=112>100,需要7次才实际绘制一次。这个情况下,实际10帧需要112*10=1120ms>1000ms才绘制完成。 this._then = this._now - this._delta % this._interval; this.update(); // ... Code for Drawing the Frame ... } } else { setTimeout(this._tick, this._interval); this.update(); } } onmove(oEvent) { oEvent.preventDefault();
if (oEvent.touches && oEvent.touches.length > 0) { oEvent.clientX = oEvent.touches[0].clientX; oEvent.clientY = oEvent.touches[0].clientY; }
this.mouseX = oEvent.clientX - (this.oDiv.offsetLeft + this.oDiv.offsetWidth / 2); this.mouseY = oEvent.clientY - (this.oDiv.offsetTop + this.oDiv.offsetHeight / 2);
this.mouseX /= 5; this.mouseY /= 5; } update() { if (!this.isStart) { return false; } var a, b; if (this.active) { a = -Math.min(Math.max(-this.mouseY, -this.size), this.size) / this.radius * this.tspeed; b = Math.min(Math.max(-this.mouseX, -this.size), this.size) / this.radius * this.tspeed; } else { a = this.lasta * 0.999; b = this.lastb * 0.999; }
this.lasta = a; this.lastb = b;
if (Math.abs(a) <= 0.01 && Math.abs(b) <= 0.01) { return; }
var c = 0; this.sineCosine(a, b, c); for (var j = 0; j < this.mcList.length; j++) { var rx1 = this.mcList[j].cx, ry1 = this.mcList[j].cy * this.ca + this.mcList[j].cz * -this.sa, rz1 = this.mcList[j].cy * this.sa + this.mcList[j].cz * this.ca, rx2 = rx1 * this.cb + rz1 * this.sb, ry2 = ry1, rz2 = rx1 * -this.sb + rz1 * this.cb, rx3 = rx2 * this.cc + ry2 * -this.sc, ry3 = rx2 * this.sc + ry2 * this.cc, rz3 = rz2;
this.mcList[j].cx = rx3; this.mcList[j].cy = ry3; this.mcList[j].cz = rz3;
var per = this.d / (this.d + rz3);
this.mcList[j].x = this.howElliptical * rx3 * per - this.howElliptical * 2; this.mcList[j].y = ry3 * per; this.mcList[j].scale = per; this.mcList[j].alpha = per;
this.mcList[j].alpha = (this.mcList[j].alpha - 0.6) * (10 / 6); }
this.doPosition(); this.depthSort(); } depthSort() { var i = 0, aTmp = [];
for (i = 0; i < this.aA.length; i++) { aTmp.push(this.aA[i]); }
aTmp.sort(function(vItem1, vItem2) { if (vItem1.cz > vItem2.cz) { return -1; } else if (vItem1.cz < vItem2.cz) { return 1; } else { return 0; } });
for (i = 0; i < aTmp.length; i++) { aTmp[i].style.zIndex = i; } } positionAll() { var phi = 0, theta = 0, max = this.mcList.length, i = 0, aTmp = [], oFragment = document.createDocumentFragment();
//随机排序 for (i = 0; i < this.aA.length; i++) { aTmp.push(this.aA[i]); }
aTmp.sort(function() { return Math.random() < 0.5 ? 1 : -1; });
for (i = 0; i < aTmp.length; i++) { oFragment.appendChild(aTmp[i]); }
this.oDiv.appendChild(oFragment);
for (var i = 1; i < max + 1; i++) { if (this.distr) { phi = Math.acos(-1 + (2 * i - 1) / max); theta = Math.sqrt(max * Math.PI) * phi; } else { phi = Math.random() * Math.PI; theta = Math.random() * (2 * Math.PI); } //坐标变换 this.mcList[i - 1].cx = this.radius * Math.cos(theta) * Math.sin(phi); this.mcList[i - 1].cy = this.radius * Math.sin(theta) * Math.sin(phi); this.mcList[i - 1].cz = this.radius * Math.cos(phi); this.aA[i - 1].style.webkitTransform = `translate(${this.mcList[i - 1].cx + this.oDiv.offsetWidth / 2 - this.mcList[i - 1].offsetWidth / 2 + 'px'},${this.mcList[i - 1].cy + this.oDiv.offsetHeight / 2 - this.mcList[i - 1].offsetHeight / 2 + 'px'}) scale(${this.mcList[i - 1].scale || 1})`; } } doPosition() { var l = this.oDiv.offsetWidth / 2, t = this.oDiv.offsetHeight / 2; for (var i = 0; i < this.mcList.length; i++) { this.aA[i].style.webkitTransform = `translate(${this.mcList[i].cx + l - this.mcList[i].offsetWidth / 2 + 'px'},${this.mcList[i].cy + t - this.mcList[i].offsetHeight / 2 + 'px'}) scale(${this.mcList[i] .scale})`; this.aA[i].style.opacity = this.mcList[i].alpha; } } sineCosine(a, b, c) { this.sa = Math.sin(a * this.dtr); this.ca = Math.cos(a * this.dtr); this.sb = Math.sin(b * this.dtr); this.cb = Math.cos(b * this.dtr); this.sc = Math.sin(c * this.dtr); this.cc = Math.cos(c * this.dtr); } } export default TagCould;