vue2前端使用图片展现视频,无法销毁请求

实现思路,使用canvas绘制图像,然后使用动画帧刷新页面

javascript 复制代码
<template>
  <canvas ref="imageCanvas" :style="canvasStyle"></canvas>
</template>

<script>
export default {
  props: {
    info: {
      type: Object,
      default: () => ({ url: '', index: '' }),
    },
  },
  data() {
    return {
      currentImage: null,
      isFullScreen: false,
      canvas: null,
      ctx: null,
      canvasStyle: {
        height: '100%',
        width: '100%',
        cursor: 'pointer',
        transition: 'transform 0.3s ease-in-out',
      },
      animationFrameId: null,
    };
  },
  mounted() {
    this.initCanvas();
    this.startAnimation();
    this.addEventListeners();
  },
  watch: {
    'info.url'(newVal) {
      if (newVal) {
        this.loadImage();
      }
    },
  },
  beforeDestroy() {
    this.stopAnimation();
    this.destroyCanvas();
    this.destroyImage();
    this.removeEventListeners();
  },
  methods: {
    initCanvas() {
      this.canvas = this.$refs.imageCanvas;
      this.ctx = this.canvas.getContext('2d');
    },
    loadImage() {
      if (!this.info || !this.info.url) return;

      this.currentImage = new Image();
      this.currentImage.onload = () => {
        this.drawCanvas();
      };
      this.currentImage.src = this.info.url;
    },
    drawCanvas() {
      if (this.currentImage) {
        this.canvas.width = this.currentImage.width;
        this.canvas.height = this.currentImage.height;
        this.ctx.drawImage(this.currentImage, 0, 0, this.canvas.width, this.canvas.height);
      }
    },
    startAnimation() {
      const animate = () => {
        this.loadImage();
        this.animationFrameId = requestAnimationFrame(animate);
      };
      this.animationFrameId = requestAnimationFrame(animate);
    },
    stopAnimation() {
      if (this.animationFrameId) {
        cancelAnimationFrame(this.animationFrameId);
        this.animationFrameId = null;
      }
    },
    addEventListeners() {
      if (this.canvas) {
        this.canvas.addEventListener('dblclick', this.toggleFullscreen);
      }
      document.addEventListener('fullscreenchange', this.handleFullscreenChange);
    },
    removeEventListeners() {
      if (this.canvas) {
        this.canvas.removeEventListener('dblclick', this.toggleFullscreen);
      }
      document.removeEventListener('fullscreenchange', this.handleFullscreenChange);
    },
    toggleFullscreen() {
      if (!this.isFullScreen) {
        if (this.canvas.requestFullscreen) {
          this.canvas.requestFullscreen();
        } else if (this.canvas.mozRequestFullScreen) {
          this.canvas.mozRequestFullScreen();
        } else if (this.canvas.webkitRequestFullscreen) {
          this.canvas.webkitRequestFullscreen();
        } else if (this.canvas.msRequestFullscreen) {
          this.canvas.msRequestFullscreen();
        }
      } else {
        if (document.exitFullscreen) {
          document.exitFullscreen();
        } else if (document.mozCancelFullScreen) {
          document.mozCancelFullScreen();
        } else if (document.webkitExitFullscreen) {
          document.webkitExitFullscreen();
        } else if (document.msExitFullscreen) {
          document.msExitFullscreen();
        }
      }
    },
    handleFullscreenChange() {
      this.isFullScreen = !this.isFullScreen;
      if (this.isFullScreen) {
        this.canvasStyle = {
          position: 'fixed',
          top: '0',
          left: '0',
          width: '100vw',
          height: '100vh',
          backgroundColor: 'rgba(0, 0, 0, 0.9)',
          cursor: 'default',
          objectFit: 'contain',
          objectPosition: 'center',
          transition: 'transform 0.3s ease-in-out',
          zIndex: 9999,
        };
        document.body.style.overflow = 'hidden';
      } else {
        this.canvasStyle = {
          height: '100%',
          width: '100%',
          cursor: 'pointer',
          transition: 'transform 0.3s ease-in-out',
        };
        document.body.style.overflow = 'auto';
      }
    },
    destroyCanvas() {
      if (this.canvas) {
        this.canvas.width = 0;
        this.canvas.height = 0;
        this.ctx = null;
        this.canvas = null;
      }
    },
    destroyImage() {
      if (this.currentImage) {
        this.currentImage.onload = null;
        this.currentImage.onerror = null;
        this.currentImage.src = '';
        this.currentImage = null;
      }
    },
  },
};
</script>

<style scoped>
/* 可以根据需要添加 scoped 样式 */
</style>
相关推荐
kyriewen8 小时前
我手写了一个 EventEmitter,面试官追问了 6 个问题——第 4 个我没答上来
前端·javascript·面试
IT_陈寒9 小时前
Java的Date类又坑了我一次,改用时间戳真香
前端·人工智能·后端
小林攻城狮9 小时前
使用 Transport 节流解决 Vercel AI SDK 流式渲染卡死问题
前端·react.js
前端缘梦9 小时前
告别 TS 运行时类型漏洞!Zod 完整入门实战教程(前端 / 全栈必备)
前端·react.js·全栈
the_answer10 小时前
Webpack vs Vite 深度对比分析
前端·webpack
转转技术团队10 小时前
验证码识别实战:前端不写页面,改训模型了?
前端
MomentYY10 小时前
Temperature:AI 的“脑洞旋钮”
前端·llm·ai编程
远航_10 小时前
OpenSpec 完整详细介绍
前端·后端
召钱熏10 小时前
状态枚举正确≠渲染正确:一个语音按钮的状态机边界修复实录
android·前端
SkyWalking中文站10 小时前
认识 Horizon UI · 1/17:SkyWalking 新一代可观测性控制台
运维·前端·监控