You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue老虎机组件加载自动启动动画报错求助

解决Vue老虎机组件自动启动报错问题

错误原因

直接调用next(this.start)时,start方法的this上下文会变成全局的window(requestAnimationFrame回调默认绑定全局对象),导致this.slots为undefined,调用map方法时自然报错。另外如果在组件未挂载完成时启动,$refs.slots也未生成,还会引发额外DOM访问错误。

解决方案

1. 选对启动时机

使用mounted生命周期钩子,此时组件已完成挂载,DOM结构和组件数据都准备完毕,能确保this指向组件实例,且$refs.slots可正常访问。

2. 保证方法的this上下文正确

直接在mounted中调用this.start(),或者用箭头函数包裹后传给next,避免this指向丢失。

修改后的完整组件代码

const slotMachine = {
    data: function () {
        return {
            slots: [{
                title: "Backend",
                items: [
                    "Jeremias Badilla",
                    "Miguel Cornejo Labra",
                    "Fabian General",
                    "Claudio Guerra",
                    "Camilo Jorquera",
                    "Ivan Nawrath Castilo"
                ]
            }],
            opts: null,
            startedAt: null
        }
    },
    // 添加mounted钩子实现自动启动
    mounted() {
        this.start();
        // 若需用requestAnimationFrame延迟启动,可改为:
        // next(() => this.start());
    },
    template: `
<div class='slot-machine'>
  <div class='slot' v-for='slot in slots' ref='slots'>
    <h2 class="slot__title">Code Contest: {{ slot.title }}</h2>
    <div class='slot__window'>
      <div class='slot__wrap'>
        <div class='slot__item' v-for='opt in slot.items'>{{ opt }}</div>
        <div class='slot__item slot__item--copy' >{{ slot.items[0] }}</div>
      </div>
    </div>
  </div>
  <button class="slot__button" @click='start'>Sortear</button>
</div>`,
    methods: {
        start: function() {
            if (this.opts) {
                return
            }         
            this.opts = this.slots.map( (data, i) => {
                const slot = this.$refs.slots[i]
                const choice = Math.floor( Math.random() * data.items.length )
                const opts = {
                    el: slot.querySelector('.slot__wrap'),
                    finalPos: choice * 180,
                    startOffset: 9000 + Math.random() * 500 + i * 500,
                    height: data.items.length * 180,
                    duration: 9000 + i * 700, // milliseconds
                    isFinished: false
                }
                return opts
            })
            // 修复animate方法的this指向
            next(this.animate.bind(this));
        },
        animate: function(timestamp) {
            if (this.startedAt == null) {
                this.startedAt = timestamp
            }
            const timeDiff = timestamp - this.startedAt
            this.opts.forEach( opt => {
                if (opt.isFinished) {
                    return
                }
                const timeRemaining = Math.max(opt.duration - timeDiff, 0)
                const power = 3
                const offset = ( Math.pow(timeRemaining, power) / Math.pow(opt.duration, power) ) * opt.startOffset
                const pos = -1 * Math.floor((offset + opt.finalPos) % opt.height)
                opt.el.style.transform = "translateY(" + pos + "px)"
                if ( timeDiff > opt.duration ) {
                    opt.isFinished = true
                }
            })
            if (this.opts.every( o => o.isFinished )) {
                this.opts = null
                this.startedAt = null
            } else {
                // 再次修复this指向
                next(this.animate.bind(this));
            }
        }
    }
}

额外修复说明

原代码中animate方法通过next(this.animate)调用时,同样存在this指向丢失问题(会指向window),所以用bind(this)绑定上下文,确保this.startedAt、this.opts等组件属性能正常访问。

内容的提问来源于stack exchange,提问作者Sinli

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 14:36:13