如何使javascript代码能够在SVG动画结束后再执行

新手上路,请多包涵

你好,我想用JavaScript写一个冒泡排序的动画,具体样式可参考VisuAlgo.net
写的时候动画用的SVG。然而实际执行过程中发现SVG的动画似乎和JavaScript的线程不是同一个线程。因此我的demo里面改变选中g label的语句在AnimateMotion动画结束之前就执行了,达不到我想要的效果。请教一下该如何解决?
代码粘贴如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>BubbleSort</title>
    <style>
        text {
            fill: black;
            font: 20px sans-serif;
            text-anchor: middle;
        }
        #sort-viz {
            width: 100%;
            text-align: center;
            overflow: hidden;
            padding-top: 10px;
        }
    </style>

<!--    <script type="text/javascript">-->
<!--        function display_alert()-->
<!--        {-->
<!--            console.log("I am an alert box!!")-->
<!--        }-->
<!--    </script>-->

</head>
<body>
    <div id="sort-viz" >
        <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="sort-svg" height="490" width="1000" >
<!--            <g >-->
<!--                <rect x="100" y="100" width="100" height="500" fill="red" fill-opacity="0.8">-->
<!--                    <animateTransform id="pause" dur="3s" type="translate" attributeType="XML" attributeName="transform"/>-->
<!--                    <animateMotion id="red" begin="pause.end"  attributeName="x" from="100,0" to="500,0" dur="3s" fill="freeze" />-->
<!--                </rect>-->
<!--                <text dy=".35em" x="50" y="450" >50</text>-->
<!--            </g>-->
<!--            <g>-->
<!--                <rect x="400" y="100" width="100" height="500" fill="green">-->
<!--                    <animate id="green" begin="red.end" onend="red.beginElement()" attributeName="x" from="500" to="100" dur="3s" fill="freeze" />-->
<!--                </rect>-->
<!--            </g>-->
        </svg>
    </div>

</body>

<script>
    class Randomize{
        Array1D(N){
            let arr=new Array();
            for(let i=0; i<N; i++){
                arr.push(parseInt(Math.random()*51,10));
            }
            return arr;
            //parseInt(Math.random()*51,10);
            //Math.floor(Math.random()*51);
        }
    }
    let randomizer=new Randomize();
    let arr=randomizer.Array1D(15);
    /*class LogTracer {
        LogTracer() {

        }

        set() {

        }

        print(message) {

        }
    }*/

    class ChartItem{
        g=document.createElementNS('http://www.w3.org/2000/svg', 'g');
        rect=document.createElementNS('http://www.w3.org/2000/svg', 'rect');
        text=document.createElementNS('http://www.w3.org/2000/svg', 'text');
        Animate=document.createElementNS('http://www.w3.org/2000/svg','animateMotion');
        isMotion=false;


        CharItem(key, value){
            this.setPosition(key, value);

            this.initRect();
            this.setHeight(value);

            this.initText();
            this.text.setAttribute('y', 5*value-15);
            this.setContent(value);

            this.g.appendChild(this.rect);
            this.g.appendChild(this.text);
        }

        initRect(){
            this.rect.setAttribute('width', '45');
            this.setFillDeactivate();
        }

        initText(){
            this.text.setAttribute('dy', '.35em');
            this.text.setAttribute('x', '22.5');
        }

        initAnimate(key){
            this.Animate.setAttribute('attributeName', 'x')
            this.Animate.setAttribute('from', '0,0');
            this.Animate.setAttribute('to', (50*key).toString()+',0');
            this.Animate.setAttribute('dur', '3s');
            this.Animate.setAttribute('fill', 'freeze');
        }

        setFillDeactivate(){
            if(!this.isMotion){
                this.rect.setAttribute('fill', 'rgb(173,216,230)');
            }
        }

        setFillActivate(){
            if(!this.isMotion){
                this.rect.setAttribute('fill', 'rgb(0, 128, 0)');
            }
        }

        removeAnimate(){
            this.g.removeChild(this.Animate);
        }

        setMotion(key){
            this.isMotion=true;
            this.initAnimate(key);
            this.g.appendChild(this.Animate);
            this.setXPosition(key);
        }

        setHeight(value){
            this.rect.setAttribute('height', 5*value);
        }

        setPosition(key, value){
            this.g.setAttribute('transform', 'translate('+50*key+','+(250-5*value)+')');
        }

        setXPosition(key){
            let str=this.g.getAttribute('transform');
            let x=str.substring(str.indexOf('(')+1, str.indexOf(','));
            let y=str.substring(str.indexOf(',')+1, str.indexOf(')'));
            x=parseInt(x)+50*key;
            y=parseInt(y);
            this.Animate.onend= () => {
                this.isMotion=false;
                this.removeAnimate();
                this.g.setAttribute('transform', 'translate('+x+','+y+')');
            }
        }

        setContent(value){
            this.text.textContent=value;
        }
    }

    class ChartTracer{
        svg=document.getElementById("sort-svg");

        ChartItems=[];
        set(array){
            for(let i=0; i<array.length; i++){
                let item=new ChartItem();
                item.CharItem(i, array[i]);
                this.ChartItems.push(item);
                this.svg.append(item.g);
            }
        }

        // reset(){
        //
        // }
        //
        // delay(){
        //
        // }
        //
        swap(index1, index2){
            // var temp=this.ChartItems[index1];
            // this.ChartItems[index1]=this.ChartItems[index2];
            // this.ChartItems[index2]=temp;
            this.ChartItems[index1].setMotion(index2-index1);
            this.ChartItems[index2].setMotion(index1-index2);
        }

        select(index){
            this.ChartItems[index].setFillActivate();
        }

        deselect(index){
            this.ChartItems[index].setFillDeactivate();
        }
        //
        // chart(){
        //
        // }
    }


    let chartTracer=new ChartTracer();
    chartTracer.set(arr);
    chartTracer.select(8);
    chartTracer.select(9);
    chartTracer.swap(8,9);
    chartTracer.deselect(8);
    chartTracer.deselect(9);



    /*class Array1DTracer{
        init(){

        }

        set(){

        }

        patch(){

        }

        depatch(){

        }

        select(){

        }

        deselect(){

        }

        chart(){

        }

        syncChartTracer(){

        }
    }*/
    i=3
    console.log("original array = ["+arr+"]")
    console.log("swap ("+i+") element: ["+arr[i]+"] and ("+(i+1)+") element: ["+arr[i+1]+"]")
    console.log("updated array = ["+arr+"]")
    //console.clear();
    function BubbleSort(arr){
        function sort(){
            let virtualArr=[arr.slice()];
            let max=arr.length;
            for(let i=0; i<max; i++){
                let done=true;
                for(let j=0; j<max-i; j++){
                    if(arr[j]>arr[j+1]){
                        let temp=arr[j+1];
                        arr[j]=arr[j+1];
                        arr[j+1]=temp;
                        done=false;
                        virtualArr.push(arr.slice());
                    }
                }
                if (done){
                    break;
                }
            }
            return virtualArr;
        }
    }
</script>

<!--
<script type="text/javascript">
    function display_alert()
    {
        console.log("I am an alert box!!")
    }

    function change_translate() {
        g.setAttribute('transform', 'translate(775, 0)');
    }

    var svg=document.getElementById('sort-svg');
    svg.onclick=function () {
        if (this.paused != true) {
            this.pauseAnimations();
            this.paused = true;
        } else  {
            this.unpauseAnimations();
            this.paused = false;
        }
    }
    var g=document.createElementNS('http://www.w3.org/2000/svg', 'g');
    g.setAttribute('transform', 'translate(175, 0)');

    var rect=document.createElementNS('http://www.w3.org/2000/svg', 'rect');
    rect.setAttribute('width', '45');
    rect.setAttribute('height', '250');
    rect.setAttribute('fill', 'rgb(173,216,230)');

    var text=document.createElementNS('http://www.w3.org/2000/svg', 'text');
    text.setAttribute('dy', '.35em');
    text.setAttribute('x', '22.5');
    text.setAttribute('y', '235');
    text.textContent=50;

    var animateM1=document.createElementNS('http://www.w3.org/2000/svg','animateMotion');
    animateM1.setAttribute('id', 'rightShift')
    animateM1.setAttribute('from', '0, 0');
    animateM1.setAttribute('to', '600,0');
    animateM1.setAttribute('dur', '5s');
    animateM1.setAttribute('fill', 'freeze');
    animateM1.setAttribute('onend', 'change_translate()')

    var animateM2=document.createElementNS('http://www.w3.org/2000/svg','animateMotion');
    animateM2.setAttribute('from', '100, 0');
    animateM2.setAttribute('to', '-500,0');
    animateM2.setAttribute('dur', '10s');
    animateM2.setAttribute('fill', 'freeze');
    animateM2.setAttribute('begin', 'rightShift.end')


    // rect.setAttribute('x', '100');
    // text.setAttribute('x', '122.5');
    g.appendChild(rect);
    g.appendChild(text);
    g.appendChild(animateM1);
    g.appendChild(animateM2);

    svg.appendChild(g);

</script>
-->

</html>
<!--
<g >
    <rect x="100" y="100" width="100" height="500" fill="red" fill-opacity="0.8">

        <animate attributeName="x" from="100" to="500" dur="3s" repeatCount="indefinite" />
    </rect>
    <text dy=".35em" x="50" y="450" >50</text>
</g>
<g>
    <rect x="400" y="100" width="100" height="500" fill="green">
        <animate attributeName="x" from="500" to="100" dur="3s" repeatCount="indefinite" />
    </rect>

</g>
-->
阅读 3.4k
1 个回答

animateMotion元素是SVGAnimationElement,它上面有一个事件endEvent,可以这样用:

先得到这个元素:

let animateElem = document.querySelector('animateMotion');

或者用你上面的方法也行:

let animateElem = document.createElementNS('http://www.w3.org/2000/svg', 'animateMotion');

然后,给它绑一个事件:

animateElem.addEventListener('endEvent', () => {
  alert('something');
})

参考资料:https://developer.mozilla.org...

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题