Gatsby服务-初始渲染时出现画布问题

时间:2020-07-11 00:17:39

标签: node.js reactjs canvas html5-canvas gatsby

我的Gatsby应用程序上的画布具有橡皮擦效果。用户可以在主页上擦除画布,以查看画布下面的视频。

一切在我的本地计算机上都可以正常工作,但是当我为应用提供服务时,画布和视频最初不会加载。但是,如果我导航到应用程序中的任何页面并返回首页,则画布和视频会加载并正常工作。

请问如何解决这个问题。

请参见以下代码:

const HomePage = () => {
  let canvas = useRef(null)
  const size = useWindowSize()
  const { currentTheme } = useGlobalStateContext()

  useEffect(() => {
    let renderingElement = canvas.current
    let drawingElement = renderingElement.cloneNode()

    let drawingCtx = drawingElement.getContext("2d")
    let renderingCtx = renderingElement.getContext("2d")

    let lastX
    let lastY

    let moving = false

    renderingCtx.globalCompositeOperation = "source-over"
    renderingCtx.fillStyle = currentTheme === "dark" ? "#000000" : "#ffffff"
    renderingCtx.fillRect(0, 0, size.width, size.height)

    renderingElement.addEventListener("mouseover", e => {
      moving = true
      lastX = e.pageX - renderingElement.offsetLeft
      lastY = e.pageY - renderingElement.offsetTop
    })

    renderingElement.addEventListener("mouseup", e => {
      moving = false
      lastX = e.pageX - renderingElement.offsetLeft
      lastY = e.pageY - renderingElement.offsetTop
    })

    renderingElement.addEventListener("mousemove", e => {
      if (moving) {
        drawingCtx.globalCompositeOperation = "source-over"
        renderingCtx.globalCompositeOperation = "destination-out"
        let currentX = e.pageX - renderingElement.offsetLeft
        let currentY = e.pageY - renderingElement.offsetTop
        drawingCtx.lineJoin = "round"
        drawingCtx.moveTo(lastX, lastY)
        drawingCtx.lineTo(currentX, currentY)
        drawingCtx.closePath()
        drawingCtx.lineWidth = 120
        drawingCtx.stroke()
        lastX = currentX
        lastY = currentY

        renderingCtx.drawImage(drawingElement, 0, 0)
      }
    })
  }, [currentTheme, size])

  return (
    <Container>
      <Video>
        <video
          height="100%"
          width="100%"
          loop
          autoPlay
          muted
          src={require("../assets/somevideo.mp4")}
        />
      </Video>
      <Canvas
        width={size.width}
        height={size.height}
        ref={canvas}
      />
    </Container>
  )
}

#useWindowSize.js

import { useState, useEffect } from "react"

export default function useWindowSize() {
  const hasWindow = typeof window !== "undefined"

  function getSize() {
    const width = hasWindow ? window.innerWidth : null
    const height = hasWindow ? window.innerHeight : null
    return {
      width,
      height,
    }
  }

  const [windowSize, setWindowSize] = useState(getSize())

  useEffect(() => {
    if (hasWindow) {
      function handleResize() {
        setWindowSize(getSize())
      }

      window.addEventListener("resize", handleResize)

      return () => window.removeEventListener("resize", handleResize)
    }
  }, [hasWindow])

  return windowSize
}

2 个答案:

答案 0 :(得分:0)

呈现服务器端时,useEffect钩子将不会运行。我怀疑useWindowSize也在使用useEffect,因此您的画布最初可能是以0x0 px绘制的。当React补水时,您在useEffect组件中的HomePage回调将捕获size(关闭)的当前引用,该引用类似于{ width: 0, height: 0 }。由于您尚未在size回调中将useEffect声明为依赖项,因此不会调用新创建的函数。

要解决此问题,您只需要向size依赖项数组中添加useEffect

您可以通过将eslint-plugin-react-hooks与eslint设置结合使用来避免将来遗漏。

答案 1 :(得分:0)

好吧,这是一个补液问题,React和Gatsby在将来自服务器的数据与您要在客户端中进行的更改进行匹配时遇到麻烦。

因此,我的主页具有在客户端而不是服务器上生成的画布动画。 我可以阻止主页渲染,直到确定客户端已完全加载为止。

所以我将索引页面编辑为以下内容,这为我解决了这个问题。

const Index = () => {
  const [isClient, setClient] = useState(false);
  const key = isClient ? "client" : "server";


  useEffect(() => {
    setClient(true);
  }, []);

  if ( !isClient ) return null;
  return (
    <div key={key}>
     <HomePage />
    </div>
  );
}