CSS转换和元素单击时的JavaScript更新之间的同步问题

4bbkushb  于 8个月前  发布在  Java
关注(0)|答案(1)|浏览(57)

我正在做一个Web项目,我有一个用户可以点击的元素列表。当一个元素被点击时,它应该成为活动的,触发一个持续200毫秒的CSS转换(例如,转换)。同时,我想更新HTML5画布上该元素的坐标。
目前,我有JavaScript代码将“active”类添加到单击的元素中,触发CSS转换,然后它调用updateItemCoordinates()函数来更新坐标。但是,坐标更新发生在CSS转换完成之后,导致转换和坐标更新之间存在滞后。或者它会保留先前的坐标。
我希望坐标更新与CSS转换同时发生。

下面是我在codepen上的例子:https://codepen.io/kilian-m/pen/QWzXmEr
下面是我目前使用的JavaScript代码:

// Lines animation
        const canvas = document.getElementById('line-canvas');
        const ctx = canvas.getContext('2d');
        const list = document.querySelector('.ressource-search__parent-terms');
        const items = Array.from(list.querySelectorAll('.ressource-search__parent-term'));
        const parent = document.querySelector('.ressource-search');

        canvas.width = list.offsetWidth;
        canvas.height = parent.offsetHeight;

        const animationDuration = 200;

        function getCanvasCoordinates(element) {
            const rect = element.getBoundingClientRect();
            const canvasRect = canvas.getBoundingClientRect();
            return {
                x: rect.left + rect.width / 2 - canvasRect.left,
                y: rect.top + rect.height / 2 - canvasRect.top
            };
        }

        let itemCoordinates = items.map(getCanvasCoordinates);

        // Click sur l'element
        items.forEach((item, key) => {
            item.addEventListener('click', () => {

                items.forEach(otherItem => {
                    otherItem.classList.remove('active');
                    otherItem.classList.add('not-active');
                });

                item.classList.remove('not-active');
                item.classList.add('active');

                updateItemCoordinates();
            });
        })

        function updateItemCoordinates() {
            const newCoordinates = items.map(getCanvasCoordinates);
            const startTime = performance.now();

            function animateTransition(timestamp) {
                const elapsed = timestamp - startTime;
                const progress = Math.min(1, elapsed / animationDuration);

                ctx.clearRect(0, 0, canvas.width, canvas.height);

                for (let i = 0; i < items.length - 1; i++) {
                    const x1 = itemCoordinates[i].x;
                    const y1 = itemCoordinates[i].y;
                    const x2 = itemCoordinates[i + 1].x;
                    const y2 = itemCoordinates[i + 1].y;

                    const newX1 = x1 + (newCoordinates[i].x - x1) * progress;
                    const newY1 = y1 + (newCoordinates[i].y - y1) * progress;
                    const newX2 = x2 + (newCoordinates[i + 1].x - x2) * progress;
                    const newY2 = y2 + (newCoordinates[i + 1].y - y2) * progress;

                    drawLine(newX1, newY1, newX2, newY2, 1);
                }

                if (progress < 1) {
                    requestAnimationFrame(animateTransition);
                } else {
                    itemCoordinates = newCoordinates;
                    cancelAnimationFrame(animateTransition);
                }
            }
            requestAnimationFrame(animateTransition);
        }

        function animateLine(index, startTime) {
            const x1 = itemCoordinates[index].x;
            const y1 = itemCoordinates[index].y;
            const x2 = itemCoordinates[index + 1].x;
            const y2 = itemCoordinates[index + 1].y;

            return function(timestamp) {
                const elapsed = timestamp - startTime;
                const progress = Math.min(1, elapsed / animationDuration);

                ctx.clearRect(0, 0, canvas.width, canvas.height);

                for (let i = 0; i < index; i++) {
                    const prevX1 = itemCoordinates[i].x;
                    const prevY1 = itemCoordinates[i].y;
                    const prevX2 = itemCoordinates[i + 1].x;
                    const prevY2 = itemCoordinates[i + 1].y;
                    drawLine(prevX1, prevY1, prevX2, prevY2, 1);
                }

                drawLine(x1, y1, x2, y2, progress);

                if (progress < 1) {
                    requestAnimationFrame(animateLine(index, startTime));
                } else {
                    if (index + 2 < items.length) {
                        animateLine(index + 1, performance.now())(performance.now());
                    }
                }
            };
        }

        function drawLine(x1, y1, x2, y2, progress) {
            ctx.beginPath();
            ctx.moveTo(x1, y1);
            ctx.lineTo(x1 + (x2 - x1) * progress, y1 + (y2 - y1) * progress);
            ctx.lineWidth = 1;
            ctx.strokeStyle = "#707070";
            ctx.stroke();
        }

        function startAnimation() {
            if (items.length > 1) {
                animateLine(0, performance.now())(performance.now());
            }
        }

        window.addEventListener('resize', () => {
            itemCoordinates = items.map(getCanvasCoordinates);
            startAnimation();
        });

        setTimeout(() => {
            startAnimation();
        }, 50);

我尝试使用transitionend事件,但它引入了延迟。我还尝试使用requestAnimationFrame在转换过程中更新坐标,但我很难正确地同步两者。

a0x5cqrl

a0x5cqrl1#

为了解决这个问题,我添加了不带转换的复制品

相关问题