Files
PC-official/static/js/tagcloud-3.1.js
DESKTOP-RQ919RC\Pc b40e69dac2 feat: 更新图片资源并优化播放器样式和布局
- 替换多张图片资源为PNG格式
- 重构底部播放器样式,调整布局和动画效果
- 优化guess页面的UI元素和交互提示
- 修复CSS中的z-index和line-height问题
- 调整音频预加载策略为auto
2025-09-23 15:52:51 +08:00

380 lines
15 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 3D标签云无缩放版
* 功能鼠标移入标签静止、自动3D旋转、GPU加速移除所有放大缩小效果
* 说明radius控制滚动区域基于translate3d实现3D定位全程保持标签原始尺寸
* 版本3.1
* */
window.tagCloud = (function (win, doc) {
function isObject(obj) {
return Object.prototype.toString.call(obj) === "[object Object]";
}
function TagCloud(options) {
var self = this;
self.config = TagCloud._getConfig(options);
self.box = self.config.element;
self.fontsize = self.config.fontsize;
self.animDuration = self.config.animDuration;
self.perspective = self.config.perspective;
// 初始化滚动区域(半径与比例)
if (Number.isInteger(self.config.radius)) {
self._radiusX = self._radiusY = self.config.radius;
} else if (self.config.radius instanceof Array) {
self._radiusX = self.config.radius.length >= 1 ? self.config.radius[0] : 60;
self._radiusY = self.config.radius.length >= 2 ? self.config.radius[1] : self._radiusX;
}
self.radius = Math.max(self._radiusX, self._radiusY);
const ratio = Math.round((self._radiusX * 10) / self._radiusY) / 10;
self.ratioX = ratio < 1 ? ratio : 1;
self.ratioY = ratio > 1 ? ratio : 1;
// 基础参数
self.depth = 2 * self.radius;
self.mspeed = TagCloud._getMsSpeed(self.config.mspeed);
self.ispeed = TagCloud._getIsSpeed(self.config.ispeed);
self.items = self._getItems();
self.direction = self.config.direction;
self.keep = self.config.keep;
// 状态变量
self.active = false;
self.mouseX = 0;
self.mouseY = 0;
self.index = -1;
self.keyframeNames = [];
// 初始化容器样式3D透视核心
self.box.style.position = "relative";
self.box.style.visibility = "visible";
// self.box.style.overflow = "hidden";
self.box.style.perspective = `${self.perspective}px`;
self.box.style.transformStyle = "preserve-3d";
// 事件绑定
self._bindEvents();
// 生成关键帧与动画
self._generateAllKeyframes();
self._applyAnimations();
// 实时更新
self._update = self._update.bind(self);
requestAnimationFrame(self._update);
}
// 静态属性与方法
TagCloud.boxs = [];
// 数组indexOf兼容
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function (elt) {
var len = this.length >>> 0;
var from = Number(arguments[1]) || 0;
from = from < 0 ? Math.ceil(from) : Math.floor(from);
if (from < 0) from += len;
for (; from < len; from++) {
if (from in this && this[from] === elt) return from;
}
return -1;
};
}
// querySelectorAll兼容
if (!doc.querySelectorAll) {
doc.querySelectorAll = function (selectors) {
var style = doc.createElement("style"),
elements = [],
element;
doc.documentElement.firstChild.appendChild(style);
doc._qsa = [];
style.styleSheet.cssText = selectors + "{x-qsa:expression(document._qsa && document._qsa.push(this))}";
window.scrollBy(0, 0);
style.parentNode.removeChild(style);
while (doc._qsa.length) {
element = doc._qsa.shift();
element.style.removeAttribute("x-qsa");
elements.push(element);
}
doc._qsa = null;
return elements;
};
}
// 配置合并移除hoverScale配置
TagCloud._getConfig = function (config) {
const defaultConfig = {
selector: ".tagcloud",
fontsize: 16,
radius: [80, 60],
mspeed: "normal",
ispeed: "normal",
direction: 135,
keep: true,
multicolour: true,
animDuration: 15,
perspective: 800
};
if (isObject(config)) {
for (let i in config) {
if (config.hasOwnProperty(i) && i !== "hoverScale") { // 禁止传入缩放相关配置
defaultConfig[i] = config[i];
}
}
}
return defaultConfig;
};
// 滚动最大速度映射
TagCloud._getMsSpeed = function (mspeed) {
const speedMap = { slow: 1.2, normal: 5, fast: 4 };
return speedMap[mspeed] || 2.5;
};
// 初始速度映射
TagCloud._getIsSpeed = function (ispeed) {
const speedMap = { slow: 8, normal: 20, fast: 40 };
return speedMap[ispeed] || 20;
};
// 角度转正弦余弦集合
TagCloud._getSc = function (a, b) {
const l = Math.PI / 180;
return [Math.sin(a * l), Math.cos(a * l), Math.sin(b * l), Math.cos(b * l)];
};
// 事件绑定兼容
TagCloud._on = function (ele, eve, handler, cap = false) {
if (ele.addEventListener) {
ele.addEventListener(eve, handler, cap);
} else if (ele.attachEvent) {
ele.attachEvent("on" + eve, handler);
} else {
ele["on" + eve] = handler;
}
};
// 原型方法
TagCloud.prototype = {
constructor: TagCloud,
// 获取标签数据(移除缩放相关逻辑)
_getItems: function () {
const self = this;
const items = [];
const children = self.box.children;
const len = children.length;
for (let i = 0; i < len; i++) {
const element = children[i];
// 强制设置标签字体大小(固定,不随深度变化)
// element.style.fontSize = `${self.fontsize}px`;
// element.style.lineHeight = "1.5"; // 固定行高,避免尺寸变化
const angle = {
phi: Math.acos(-1 + (2 * i + 1) / len),
theta: Math.sqrt((len + 1) * Math.PI) * Math.acos(-1 + (2 * i + 1) / len)
};
// 初始3D坐标球面映射
const x = (self.radius / 2) * 1.5 * Math.cos(angle.theta) * Math.sin(angle.phi) * self.ratioX;
const y = (self.radius / 2) * 1.5 * Math.sin(angle.theta) * Math.sin(angle.phi) / self.ratioY;
const z = (self.radius / 2) * 1.5 * Math.cos(angle.phi);
// 彩色标签配置
if (self.config.multicolour) {
const hue = Math.round(Math.random() * 360);
const lightness = 30 + Math.round(Math.random() * 30);
element.style.color = `hsl(${hue}, 100%, ${lightness}%)`;
}
// 标签基础样式移除scale过渡固定尺寸
element.style.position = "absolute";
element.style.transformStyle = "preserve-3d";
element.style.transition = "transform 0.3s ease, z-index 0.3s ease, opacity 0.3s ease"; // 仅保留位置、层级、透明度过渡
element.style.cursor = "pointer";
element.style.transform = "scale(1)"; // 强制固定缩放为1无放大缩小
element.index = i;
items.push({
element,
angle,
x,
y,
z,
offsetWidth: element.offsetWidth,
offsetHeight: element.offsetHeight
});
}
return items;
},
// 绑定交互事件(无缩放逻辑)
_bindEvents: function () {
const self = this;
// 容器鼠标移入/移出
TagCloud._on(self.box, "mouseover", () => { self.active = true; });
TagCloud._on(self.box, "mouseout", () => { self.active = false; });
// 鼠标移动
const target = self.config.keep ? win : self.box;
TagCloud._on(target, "mousemove", (ev) => {
const oEvent = win.event || ev;
const rect = self.box.getBoundingClientRect();
self.mouseX = (oEvent.clientX - (rect.left + rect.width / 2)) / 8;
self.mouseY = (oEvent.clientY - (rect.top + rect.height / 2)) / 8;
});
// 标签鼠标移入/移出(仅静止、置顶,无放大)
self.items.forEach(item => {
TagCloud._on(item.element, "mouseover", function () {
self.index = this.index;
this.style.zIndex = 999; // 仅置顶,不放大
});
TagCloud._on(item.element, "mouseout", function () {
self.index = -1;
this.style.zIndex = "";
});
});
},
// 生成关键帧移除缩放逻辑固定scale=1
_generateKeyframe: function (itemIndex) {
const self = this;
const keyframeName = `tagCloudAnim_${Date.now()}_${itemIndex}`;
const styleSheet = doc.createElement("style");
let keyframeCSS = `@keyframes ${keyframeName} {`;
// 拆分360°旋转为20个关键帧
for (let deg = 0; deg <= 360; deg += 18) {
const rad = deg * Math.PI / 180;
const item = self.items[itemIndex];
const sc = TagCloud._getSc(
Math.sin(rad) * self.mspeed,
Math.cos(rad) * self.mspeed
);
// 实时3D坐标计算
const rx1 = item.x;
const ry1 = item.y * sc[1] + item.z * -sc[0];
const rz1 = item.y * sc[0] + item.z * sc[1];
const rx2 = rx1 * sc[3] + rz1 * sc[2];
const ry2 = ry1;
const rz2 = rz1 * sc[3] - rx1 * sc[2];
// 固定透明度(或按深度微调,不影响尺寸)
const per = self.depth / (self.depth + rz2);
const opacity = Math.max(0.5, per); // 仅调整透明度,无缩放
// 容器中心偏移
const centerX = (self.box.offsetWidth - item.offsetWidth) / 2;
const centerY = (self.box.offsetHeight - item.offsetHeight) / 2;
// 关键帧内容固定scale=1仅保留translate3d和opacity
const percent = (deg / 360) * 100;
keyframeCSS += `${percent}% {
transform: translate(${rx2 + centerX}px, ${ry2 + centerY}px) scale(1);
opacity: ${opacity};
z-index: ${Math.ceil(per * 10)};
}`;
}
keyframeCSS += `}`;
styleSheet.innerHTML = keyframeCSS;
doc.head.appendChild(styleSheet);
return keyframeName;
},
// 生成所有标签的关键帧
_generateAllKeyframes: function () {
const self = this;
self.keyframeNames = self.items.map((_, index) => {
return self._generateKeyframe(index);
});
},
// 为标签应用动画(无缩放)
_applyAnimations: function () {
const self = this;
self.items.forEach((item, index) => {
const animName = self.keyframeNames[index];
// 应用动画固定scale=1仅translate3d和opacity变化
item.element.style.animation = `${animName} ${self.animDuration}s linear infinite`;
item.element.style.transform = `scale(1)`; // 双重保障,防止缩放
});
},
// 实时更新(仅静止、调整动画速度,无放大缩小)
_update: function () {
const self = this;
if (self.index === -1 && !self.active) {
requestAnimationFrame(self._update);
return;
}
// 选中标签:仅静止、置顶、保持原始尺寸
if (self.index !== -1) {
const item = self.items[self.index];
const centerX = (self.box.offsetWidth - item.offsetWidth) / 2;
const centerY = (self.box.offsetHeight - item.offsetHeight) / 2;
// 暂停动画+固定位置+scale=1
item.element.style.animationPlayState = "paused";
item.element.style.transform = `translate(${centerX}px, ${centerY}px) scale(1)`;
item.element.style.opacity = 1;
} else {
// 鼠标交互:仅调整动画速度,无缩放
// self.items.forEach((item, index) => {
// item.element.style.animationPlayState = "running";
// const newDuration = self.animDuration - Math.abs(self.mouseX + self.mouseY) / 5;
// const animName = self.keyframeNames[index];
// item.element.style.animation = `${animName} ${Math.max(8, newDuration)}s linear infinite`;
// item.element.style.transform = `scale(1)`; // 确保无缩放
// });
}
requestAnimationFrame(self._update);
},
// 销毁实例
destroy: function () {
const self = this;
// 移除所有动画样式
const styles = doc.querySelectorAll('style');
styles.forEach(style => {
if (style.innerHTML.includes('tagCloudAnim_')) {
doc.head.removeChild(style);
}
});
// 移除事件监听
self.box.onmouseover = null;
self.box.onmouseout = null;
self.items.forEach(item => {
item.element.onmouseover = null;
item.element.onmouseout = null;
item.element.style.animation = null;
item.element.style.transform = "scale(1)";
});
// 从实例数组中移除
const idx = TagCloud.boxs.indexOf(self.box);
if (idx !== -1) TagCloud.boxs.splice(idx, 1);
}
};
// 工厂函数
return function (options = {}) {
const selector = options.selector || ".tagcloud";
const elements = doc.querySelectorAll(selector);
const instances = [];
elements.forEach(element => {
if (TagCloud.boxs.indexOf(element) === -1) {
TagCloud.boxs.push(element);
const instance = new TagCloud({ ...options, element });
instances.push(instance);
}
});
return instances;
};
})(window, document);