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

Vue项目中PIXI.js数据变更时清除画布重绘及添加过渡效果问题

问题根因

你之前调用this.app.renderer.destroy()、this.app.renderer.clean()无效的核心原因是,这两个API是针对渲染器本身的销毁/帧缓存清理操作,并不会移除PIXI舞台(stage)中已经添加的子元素实例,你每次调用visualizeStuff都往舞台新增一批路径和精灵,旧实例一直保留,自然会出现内容叠加的问题。


问题1:旧内容无法清除的解决方案

首先优化元素管理逻辑,不要直接把动态生成的元素挂载到根stage上,新增一个专属的动态容器统一管理所有可变元素:

// data 中新增动态容器变量
data() {
  return {
    app: new PIXI.Application({
      width: 800,
      height: 600,
      transparent: true
    }),
    currentYear: 2021,
    dynamicContainer: null, // 存储所有动态生成的元素
    // ...other data...
  }
},
mounted() {
  this.width = d3.select(this.$refs.container).node().getBoundingClientRect().width
  this.height = d3.select(this.$refs.container).node().getBoundingClientRect().height
  // 初始化动态容器并挂载到根舞台,只执行一次
  this.dynamicContainer = new PIXI.Container()
  this.app.stage.addChild(this.dynamicContainer)
  // canvas 挂载也只执行一次,不要放到 visualizeStuff 里
  this.$el.appendChild(this.app.view)
  this.visualizeStuff()
},

然后将原来往this.app.stage添加元素的逻辑,全部改为往this.dynamicContainer添加,重绘前调用removeChildren()清空容器即可:

watch: {
  params: function(newVal, oldVal) {
    if (newVal.someValue == this.target) {
      this.currentYear = newVal.year
      // 清空所有旧动态元素
      this.dynamicContainer.removeChildren()
      this.visualizeStuff()
    }
  }
},
methods: {
  visualizeStuff() {
    this.app.renderer.resize(this.width, this.height)
    // ...过滤数据逻辑不变
    for (let i = 0; i < filteredData.length; i++) {
      let path = new PIXI.Graphics()
      path.lineStyle(1, 0xff0000, 0)
      this.pathGenerator(filteredData[i].geometry, path)
      path.endFill()
      // 改为加到动态容器
      this.dynamicContainer.addChild(path)

      let item = PIXI.Sprite.from(require("images/item.png"))
      // 改为加到动态容器
      this.dynamicContainer.addChild(item)
      // ...GSAP 路径动画逻辑不变
    }
  }
}

问题2:添加过渡效果的实现方案

你已经在项目中使用GSAP,可以直接通过GSAP的透明度动画实现退场/入场效果,修改watch和元素生成逻辑即可:

// watch 中添加退场动画
watch: {
  params: async function(newVal, oldVal) {
    if (newVal.someValue == this.target) {
      this.currentYear = newVal.year
      // 存在旧元素时先执行退场动画
      if (this.dynamicContainer.children.length) {
        await gsap.to(this.dynamicContainer.children, {
          alpha: 0,
          duration: 0.3,
          ease: "power1.out"
        })
        // 动画结束后清空旧元素
        this.dynamicContainer.removeChildren()
      }
      this.visualizeStuff()
    }
  }
},
methods: {
  visualizeStuff() {
    this.app.renderer.resize(this.width, this.height)
    // ...过滤数据逻辑不变
    for (let i = 0; i < filteredData.length; i++) {
      let path = new PIXI.Graphics()
      path.lineStyle(1, 0xff0000, 0)
      this.pathGenerator(filteredData[i].geometry, path)
      path.endFill()
      path.alpha = 0 // 初始透明度设为0
      this.dynamicContainer.addChild(path)

      let item = PIXI.Sprite.from(require("images/item.png"))
      item.alpha = 0 // 初始透明度设为0
      this.dynamicContainer.addChild(item)
      // ...GSAP 路径动画逻辑不变
    }
    // 所有新元素生成完成后执行入场动画
    gsap.to(this.dynamicContainer.children, {
      alpha: 1,
      duration: 0.3,
      ease: "power1.in",
      stagger: 0.05 // 可选:元素错开入场,不需要可删除
    })
  }
}

注意事项

  • 组件销毁时需要调用this.app.destroy(true)清理PIXI实例,避免内存泄漏
  • PIXI实例不要放在Vue的响应式对象中,避免被Proxy代理导致性能问题或异常
  • 如果路径动画是持续运行的,清空元素前需要先销毁对应GSAP动画实例,避免内存泄漏

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 11:24:03