tagCould3d 移动端优化版
针对https://github.com/bitjjj/JS-3D-TagCloud这个版本的做了移动端性能优化(使用transform做偏移及缩放,优化帧)。基本原理一致。
兼容移动端和PC端版本
class TagCould { mcList = []; active = false; // 事件控制 lasta = 0.3; lastb = 0.3; distr = true; mouseX = 0; mouseY = 0; aA = null; oDiv = null; _now = 0; _then = Date.now(); _delta = 0; _isMobile = /mobile/gi.test(navigator.userAgent); _isCreate = false; _event = { start: this._isMobile ? 'touchstart' : 'mouseover', move: this._isMobile ? 'touchmove' : 'mousemove', end: this._isMobile ? 'touchend' : 'mouseout' }; isStart = false; defaultOptions = { auto: true, animate: false, dtr: Math.PI / 180, d: 500, // 深度 tspeed: 2, // 旋转速度 size: 250, howElliptical: 1, fps: 60, 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(); this.startEvent = this.startEvent.bind(this); this.endEvent = this.endEvent.bind(this); this.moveEvent = this.moveEvent.bind(this); } init() { // 如果手动触发,则需要调用此函数 if (this.auto) { this.create(); } } play() { this.isStart = true; } pause() { this.isStart = false; } destroy() { document.removeEventListener(this._event.start, this.startEvent, false); document.removeEventListener(this._event.end, this.endEvent, false); document.removeEventListener(this._event.move, this.moveEvent, false); this.oDiv.innerHTML = ''; this.mcList = []; this._isCreate = false; this.isStart = false; } create() { // 只在auto为false且isStart为false时触发 if (this._isCreate) { return false; } this.createTag(); this.sineCosine(0, 0, 0); this.setOffset(); this.positionAll(); this.tiggerEvent(); } tiggerEvent() { this.tick(); this.bindEvent(); } 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); } if (this.animate) { this.oDiv.style.transform = 'scale(0)'; this.oDiv.style.opacity = '0'; this.oDiv.style.transition = `all 0.5s`; } } getContainer() { return this.oDiv; } getTags() { return this.oDiv.querySelectorAll('.tag'); } 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(this._event.start, this.startEvent, false); document.addEventListener(this._event.end, this.endEvent, false); document.addEventListener(this._event.move, this.moveEvent, false); } startEvent() { this.active = true; } endEvent() { this.active = false; } moveEvent(evt) { this.onmove(window.event || evt); } 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 /= 15; // 减速 this.mouseY /= 15; /* 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 * 1; b = this.lastb * 1; } 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(); this._isCreate = true; //随机排序 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})`; } if (this.animate) { let timer = setTimeout(() => { clearTimeout(timer); this.oDiv.style.transform = 'scale(1)'; this.oDiv.style.opacity = `1`; },100); } } 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; this.aA[i].style.top = 0; this.aA[i].style.left = 0; } } 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); } } iENV.Emitter.mixTo(TagCould); export default TagCould;
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;