Продукт медленно вращается на мобильных устройствах

#vue.js

#vue.js

Вопрос:

я изо всех сил пытаюсь понять, где мой скрипт замедляется на мобильных устройствах. Если щелкнуть и перетащить изображение, оно будет вращаться, сделайте то же самое на мобильных устройствах, и это действительно медленно!

Я переместил скрипт на его собственную страницу, но не могу добавить его сюда, поскольку запросы AJAX, похоже, не работают, и есть большой массив изображений, которые извлекаются.

Моя проблема просто в том, что перемещение не перемещается во времени с вашим пальцем…

http://click.martynleeball.co.uk/360/

Это сценарий:

 Vue.component('viewer', {
    data() {
        return {
            loading: false,

            loop: true,
            speed: 2,
            reverse: true,

            speedController: 0,

            zoomEnabled: true,
            zoomLevels: [1, 1.5, 2, 2.5, 3],
            zoomLevel: 1,

            frame: 1,
            images: [],
            imagesPreloaded: 0,

            spinEnabled: true,
            spinAuto: false,

            viewportScale: 0.3,
            viewportEnabled: true,
            viewportOpacity: 0.8,

            lastX: 0,
            lastY: 0,

            startX: 0,
            startY: 0,

            translateX: 0,
            translateY: 0,

            isMoving: false,
            isDragging: false,

            lastPinch: 0,

            animationRequestID: 0,

            spinStart: null,
            spinThen: Date.now(),
            fps: 1000 / 8,

            $moveEvent: null
        };
    },
    mounted() {
        window.addEventListener('mouseup', this.handleEnd);
        window.addEventListener('touchend', this.handleEnd);
        window.addEventListener('orientationchange', this.fetch);

        // iOS Specific
        this.$el.addEventListener('gesturestart', function (e) {
            e.preventDefault();
        });

        this.fetch();
    },
    beforeDestroy() {
        window.removeEventListener('mouseup', this.handleEnd);
        window.removeEventListener('touchend', this.handleEnd);
    },
    methods: {
        fetch: function () {
            
            const self = this;

            // Reset the preload
            this.imagesPreloaded = 0;
            this.loading = true;

            var xhttp = new XMLHttpRequest();
            xhttp.onreadystatechange = function () {
                if (this.readyState == 4 amp;amp; this.status == 200) {

                    // All of the 360 images.
                    self.images = JSON.parse(this.responseText);
                    
                    var images = [];
                    for (var i = 0; i < self.images.length; i  ) {
                        images[i] = new Image();
                        images[i].src = self.images[i][1].url;
                        
                        self.imagesPreloaded  ;
                    }

                    // Some dealers spin the vehicle the wrong way...
                    if (self.reverse) {
                        self.images = self.images.reverse();
                    }
                    
                    self.loading = false;
                    
                    if (self.spinAuto amp;amp; self.spinEnabled) {
                        self.spinStart = self.spinThen;
                        self.spin(1);
                    }
                }
            };
            xhttp.open("GET", "./images.php", true);
            xhttp.send();
        },
        handleStart($event) {
            if ($event.button amp;amp; $event.button !== 0) {
                return;
            }
            if (this.animationRequestID !== 0) {
                this.spinStop();
            }
            this.isMoving = true;
            this.isDragging = true;

            this.startX = $event.pageX || $event.touches[0].pageX;
            this.startY = $event.pageY || $event.touches[0].pageY;
        },
        handleMove($event, viewport) {
            if ($event.button amp;amp; $event.button !== 0) {
                return;
            }
            if ($event.touches amp;amp; $event.touches.length > 1) {
                this.zoomPinch($event);
                return;
            }

            this.$moveEvent = $event;

            if (this.isMoving amp;amp; this.isDragging) {
                const positions = {
                    x: $event.pageX || $event.touches[0].pageX,
                    y: $event.pageY || $event.touches[0].pageY
                }

                if (this.zoomLevel !== 1) {
                    this.translate(positions, null, viewport);
                }
                if (this.zoomLevel === 1) {
                    this.changeFrame(positions, $event.touches amp;amp; $event.touches.length >= 1);
                }

                this.lastX = positions.x;
                this.lastY = positions.y;
            }
        },
        handleEnd: function () {
            this.lastPinch = 0;
            this.isMoving = false;
        },

        spin(index) {
            let i = index;
            if (i >= this.images.length) {
                i = 1;
            }
            this.animationRequestID = window.requestAnimationFrame(() => this.spin(i));

            let now = Date.now();
            let elapsed = now - this.spinThen;

            if (elapsed > this.fps) {
                this.spinThen = now - (elapsed % this.fps);
                this.frame = i;
                i  = 1;
            }
        },
        spinToggle: function () {
            if (this.animationRequestID === 0 amp;amp; this.zoomLevel === 1) {
                this.spin(this.frame);
                return;
            }
            this.spinStop();
        },
        spinStop: function () {
            if (this.animationRequestID) {
                window.cancelAnimationFrame(this.animationRequestID);
                this.animationRequestID = 0;
            }
        },

        translate(positions, zooming, viewport) {
            if (this.$moveEvent) {
                this.$moveEvent.preventDefault();
            }
            window.requestAnimationFrame(() => {
                positions = positions || {
                    x: this.startX,
                    y: this.startY
                };

                if (viewport) {
                    this._translateFromViewport(positions);
                } else {
                    this._translateFromImage(positions, zooming);
                }

                this.startX = positions.x;
                this.startY = positions.y;
            });
        },

        /**
         * @param positions
         * @private
         */
        _translateFromViewport(positions) {
            let move = {
                x: Math.floor(positions.x - this.startX),
                y: Math.floor(positions.y - this.startY)
            };

            let box = this.$refs.viewportBox.getBoundingClientRect();
            let container = this.$refs.viewportContainer.getBoundingClientRect();

            // Amount of pixels moved within animation frame, adjust based on viewport scale.
            // Zoom level doesn't matter as image scale doesn't move, so box is moving same amount of pixels.
            let moveAmountX = (move.x / this.viewportScale);
            let moveAmountY = (move.y / this.viewportScale);

            // Find the current offset of the container bounds, calculate new offset based on movement amount
            let calculatedOffset = {
                left: (container.left - box.left) - moveAmountX,
                right: (container.right - box.right) - moveAmountX,
                top: (container.top - box.top) - moveAmountY,
                bottom: (container.bottom - box.bottom) - moveAmountY
            };

            // Only move if the calculated new offset is not out of container bounds
            // Reverse the movement as moving box in same direction as cursor rather than the image.
            if (calculatedOffset.left <= 0 amp;amp; calculatedOffset.right >= 0) {
                this.translateX  = -moveAmountX;
            }
            if (calculatedOffset.top <= 0 amp;amp; calculatedOffset.bottom >= 0) {
                this.translateY  = -moveAmountY;
            }

        },
        _translateFromImage(positions, zooming) {
            let move = {
                x: Math.floor(positions.x - this.startX),
                y: Math.floor(positions.y - this.startY)
            };

            let image = this.$refs.image.getBoundingClientRect();
            let container = this.$refs.container.getBoundingClientRect();

            let moveAmountX = move.x * this.zoomLevel;
            let moveAmountY = move.y * this.zoomLevel;

            let calculatedOffset = {
                left: (container.left - image.left) - moveAmountX,
                right: (container.right - image.right) - moveAmountX,
                top: (container.top - image.top) - moveAmountY,
                bottom: (container.bottom - image.bottom) - moveAmountY
            };

            if (zooming) {
                if (calculatedOffset.left <= 0) {
                    this.translateX  = calculatedOffset.left;
                }
                if (calculatedOffset.right >= 0) {
                    this.translateX  = calculatedOffset.right;
                }
                if (calculatedOffset.top <= 0) {
                    this.translateY  = calculatedOffset.top;
                }
                if (calculatedOffset.bottom >= 0) {
                    this.translateY  = calculatedOffset.bottom;
                }
            }

            if (calculatedOffset.left >= 0 amp;amp; calculatedOffset.right <= 0) {
                this.translateX  = move.x / this.zoomLevel;
            }

            if (calculatedOffset.top >= 0 amp;amp; calculatedOffset.bottom <= 0) {
                this.translateY  = move.y / this.zoomLevel;
            }
        },

        changeFrame(positions, touch) {
            this.speedController  = 1;
            if ((this.speedController < this.speed) amp;amp; !touch) {
                return;
            }
            if (this.speedController > this.speed) {
                this.speedController = 0;
            }

            if (positions.x > this.lastX) {
                if (this.frame >= 0 amp;amp; this.frame < this.images.length) {
                    this.frame  = 1;
                } else if (this.loop) {
                    this.frame = 1;
                }
            } else if (positions.x < this.lastX) {
                if (this.frame >= 0 amp;amp; this.frame - 1 > 0) {
                    this.frame -= 1;
                } else if (this.loop) {
                    this.frame = this.images.length;
                }
            }
        }
    },
    watch: {
        zoomLevel: function () {
            if (this.zoomLevel !== 1 amp;amp; this.animationRequestID !== 0) {
                this.spinStop();
            }
        }
    },
    computed: {
        closestZoom: function () {
            return this.zoomLevels.reduce((a, b) => {
                return Math.abs(b - this.zoomLevel) < Math.abs(a - this.zoomLevel) ? b : a;
            });
        },
        imageSet: function () {
            return this.images.map(image => {
                return image[this.closestZoom].url;
            });
        },
        preloadProgress: function () {
            return Math.floor(this.imagesPreloaded / this.images.length * 100);
        },
        currentPath: function () {
            return this.images[this.frame - 1][this.closestZoom].url;
        },
        nextZoomLevel: function () {
            if (this.zoomLevels.indexOf(this.closestZoom) === this.zoomLevels.length - 1) {
                return this.zoomLevels[0];
            }
            return this.zoomLevels[this.zoomLevels.indexOf(this.closestZoom)   1];
        },
        viewportTransform: function () {
            if (this.viewportEnabled) {
                let translateX = -((this.translateX * this.viewportScale) * this.zoomLevel);
                let translateY = -((this.translateY * this.viewportScale) * this.zoomLevel);

                return `scale(${1 / this.zoomLevel}) translateX(${translateX}px) translateY(${translateY}px)`;
            }
        },
        transform: function () {
            return `scale(${this.zoomLevel}) translateX(${this.translateX}px) translateY(${this.translateY}px)`;
        },
        canZoomIn: function () {
            return this.zoomLevels[this.zoomLevels.indexOf(this.closestZoom)   1] === undefined
        },
        canZoomOut: function () {
            return this.zoomLevels[this.zoomLevels.indexOf(this.closestZoom)   -1] === undefined
        }
    },
    template: '#template'
});
  

Ответ №1:

Удалось найти ответ, при включении обновления мобильного браузера прокрутка имеет более высокий приоритет, чем другие события касания, поэтому добавление touch-action: none; к моему изображению устраняет эту проблему!