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
相关产品推荐
相关产品推荐

