我的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
}
答案 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>
);
}