Image 背景图像未以奇怪的方式显示在画布上

Image 背景图像未以奇怪的方式显示在画布上,image,vue.js,canvas,vuejs3,Image,Vue.js,Canvas,Vuejs3,我正在编写一个贺卡生成器,以便在VueJS3中进行培训。一切正常,除了一件事,请看我的代码: <template> <div> <h1>greeting card generator</h1> <div class="board"> <canvas id='myCanvas' :width="size.w" :height="size.h&quo

我正在编写一个贺卡生成器,以便在VueJS3中进行培训。一切正常,除了一件事,请看我的代码:

<template>
  <div>
    <h1>greeting card generator</h1>
    <div class="board">
        <canvas id='myCanvas' :width="size.w" :height="size.h" tabindex='0'
style="border:1px solid #000000;"
        ></canvas>
    </div>
    <textarea
      :style="'width:' + size.w + 'px; resize:none;'"
      v-model="texte"
      placeholder="Write your text here">
    </textarea>
  </div>
</template>

<script>

import {
  defineComponent, onMounted, ref, reactive, watch,
} from 'vue';

export default defineComponent({
  setup() {
    const myCanvas = ref(null);
    const texte = ref('');
    const rapport = ref(0);

    const size = reactive({
      w: window.innerWidth * 0.8,
      h: (window.innerWidth * 0.8) / 1.8083832335329342,
    });

    function drawText() {
      const fontSize = 0.05 * window.innerWidth - 10;
      myCanvas.value.font = `${fontSize}px Adrip`;
      myCanvas.value.textAlign = 'center';
      const x = size.w / 2;
      const lineHeight = fontSize;
      const lines = texte.value.split('\n');
      for (let i = 0; i < lines.length; i += 1) {
        myCanvas.value.fillText(
          lines[lines.length - i - 1],
          x,
          (size.h * 0.98) - (i * lineHeight),
        );
      }
    }

    function initCarte() {
      const background = new Image();
      background.src = '/img/fond.jpeg';
      background.onload = function () {
        rapport.value = background.naturalWidth / background.naturalHeight;
        size.h = size.w / rapport.value;
        try {
          myCanvas.value.drawImage(background, 0, 0, size.w, size.h);
        } catch (e) {
          console.log(`ERREUR DE CHARGEMENT D'IMAGE: ${e}`);
        }
        drawText();
      };
    }

    function handleResize() {
      size.w = window.innerWidth * 0.8;
      size.h = size.w / rapport.value;
      initCarte();
    }

    window.addEventListener('resize', handleResize);

    onMounted(() => {
      const c = document.getElementById('myCanvas');
      const ctx = c.getContext('2d');
      myCanvas.value = ctx;
      initCarte();
    });

    watch(texte, () => {
      initCarte();
    });

    return {
      myCanvas,
      size,
      texte,
    };
  },
});

</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
@font-face {
  font-family: 'Adrip';
  src: local('Adrip'), url('/fonts/adrip1.ttf') format('truetype');
}

#myCanvas {
  border: 1px solid grey;
}
</style>
如果我不硬编码,只输入规范值
window.innerWidth*0.8
,图像不会显示,尽管
size.h=size.w/rapport.value行正确执行

我真的不明白这种行为,有人能给我解释一下吗? 另外,如果有人知道如何一次性加载图像,这样我就不必在每次刷新时都加载图像,那就更好了:)


提前谢谢

您的问题是,由于模板魔术的工作方式,在绘制图像后更改画布的大小。如果将
debugger
放在
drawText()后面
在后台onload函数中,您将看到它实际上绘制了图像。但是,在同一个函数中,您可以设置
size.h
<代码>大小
是反应性的,因此标记为“脏”<代码>大小也用于模板中,因此模板被标记为脏。执行onload函数后,Vue将重新加载模板。。。并删除你的图像

我认为您最好在这里使用
nextTick
。您需要谨慎地使用它,但我认为这是一种您别无选择只能等待DOM解决的情况。要执行此操作,请从vue导入
nextTick

import { nextTick } from 'vue';
然后围绕您的
drawImage
尝试用它捕捉块

background.onload = function () {
  rapport.value = background.naturalWidth / background.naturalHeight;
  size.h = size.w / rapport.value;
  nextTick(() => {
    try {
      myCanvas.value.drawImage(background, 0, 0, size.w, size.h);
    } catch (e) {
      console.log(`ERREUR DE CHARGEMENT D'IMAGE: ${e}`);
    }
    drawText();
  });
};
至于你的最后一个问题,如何加载图像一次。。。简单的回答是。。。你不能。每当画布更改时,都需要重新绘制它。至少浏览器应该缓存图像,所以它只是从缓存中提取图像,而不是执行另一个http请求


您的问题是,由于模板魔术的工作方式,在绘制图像后更改画布的大小。如果将
debugger
放在
drawText()后面
在后台onload函数中,您将看到它实际上绘制了图像。但是,在同一个函数中,您可以设置
size.h
<代码>大小是反应性的,因此标记为“脏”<代码>大小也用于模板中,因此模板被标记为脏。执行onload函数后,Vue将重新加载模板。。。并删除你的图像

我认为您最好在这里使用
nextTick
。您需要谨慎地使用它,但我认为这是一种您别无选择只能等待DOM解决的情况。要执行此操作,请从vue导入
nextTick

import { nextTick } from 'vue';
然后围绕您的
drawImage
尝试用它捕捉块

background.onload = function () {
  rapport.value = background.naturalWidth / background.naturalHeight;
  size.h = size.w / rapport.value;
  nextTick(() => {
    try {
      myCanvas.value.drawImage(background, 0, 0, size.w, size.h);
    } catch (e) {
      console.log(`ERREUR DE CHARGEMENT D'IMAGE: ${e}`);
    }
    drawText();
  });
};
至于你的最后一个问题,如何加载图像一次。。。简单的回答是。。。你不能。每当画布更改时,都需要重新绘制它。至少浏览器应该缓存图像,所以它只是从缓存中提取图像,而不是执行另一个http请求


谢谢您的完整回答。我学习了“nextTick”,这是我以前不知道的:)谢谢你给我完整的答案。我学会了“nextTick”,这是我以前不知道的:)