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(containertags = [], options = {}) {
        this.container = container;
        this.tags = tags;
        options = Object.assign(this.defaultOptionsoptions);
        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(000);
        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 = 0i < this.tags.lengthi++) {
            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 = 0i < this.aA.lengthi++) {
            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._tickthis._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 ab;
        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(abc);
        for (var j = 0j < this.mcList.lengthj++) {
            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 = 0i < this.aA.lengthi++) {
            aTmp.push(this.aA[i]);
        }

        aTmp.sort(function(vItem1vItem2) {
            if (vItem1.cz > vItem2.cz) {
                return -1;
            } else if (vItem1.cz < vItem2.cz) {
                return 1;
            } else {
                return 0;
            }
        });

        for (i = 0i < aTmp.lengthi++) {
            aTmp[i].style.zIndex = i;
        }
    }
    positionAll() {
        var phi = 0,
            theta = 0,
            max = this.mcList.length,
            i = 0,
            aTmp = [],
            oFragment = document.createDocumentFragment();

        //随机排序
        for (i = 0i < this.aA.lengthi++) {
            aTmp.push(this.aA[i]);
        }

        aTmp.sort(function() {
            return Math.random() < 0.5 ? 1 : -1;
        });

        for (i = 0i < aTmp.lengthi++) {
            oFragment.appendChild(aTmp[i]);
        }

        this.oDiv.appendChild(oFragment);

        for (var i = 1i < max + 1i++) {
            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 = 0i < this.mcList.lengthi++) {
            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(abc) {
        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;
posted @ 2020-09-18 19:17  黑客PK  阅读(162)  评论(0编辑  收藏  举报