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

问题描述 投票:0回答:1

我正在开发一个网络项目,其中有用户可以单击的元素列表。单击某个元素时,它应该变为活动状态,从而触发持续 200 毫秒的 CSS 转换(例如,变换)。同时,我想更新该元素在 HTML5 画布上的坐标。

目前,我有 JavaScript 代码,将“active”类添加到单击的元素,触发 CSS 转换,然后调用 updateItemCooperatives() 函数来更新坐标。但是,坐标更新发生在 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 在过渡期间更新坐标,但我很难正确同步两者。

javascript css canvas css-transitions
1个回答
0
投票

我添加了透明克隆而不进行过渡来解决我的问题

© www.soinside.com 2019 - 2024. All rights reserved.