forked from zhongyidian/yx-react-template
feat: 完善课程模板通用能力
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
1e02c7835e
commit
a641d0e2ea
70 files changed
+2868
-205
No files matched your search
@@ -0,0 +1,134 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { CanvasContext } from "./CanvasContext";
|
||||
import type {
|
||||
CourseCanvasContextValue,
|
||||
CourseCanvasProps,
|
||||
CourseCanvasViewport,
|
||||
} from "./CourseCanvas.types";
|
||||
import { useCanvasScale } from "./useCanvasScale";
|
||||
|
||||
const DEFAULT_WIDTH = 1920;
|
||||
const DEFAULT_HEIGHT = 1080;
|
||||
|
||||
export function CourseCanvas({
|
||||
children,
|
||||
width = DEFAULT_WIDTH,
|
||||
height = DEFAULT_HEIGHT,
|
||||
maxScale = 1,
|
||||
className,
|
||||
canvasClassName,
|
||||
portalClassName,
|
||||
}: CourseCanvasProps) {
|
||||
const scale = useCanvasScale({ width, height, maxScale });
|
||||
const frameRef = useRef<HTMLDivElement>(null);
|
||||
const portalContainer = useRef<HTMLDivElement>(null);
|
||||
const [viewport, setViewport] = useState<CourseCanvasViewport>({
|
||||
width,
|
||||
height,
|
||||
scale,
|
||||
originLeft: 0,
|
||||
originTop: 0,
|
||||
});
|
||||
|
||||
const measure = useCallback(() => {
|
||||
const frame = frameRef.current;
|
||||
if (!frame) return;
|
||||
const rect = frame.getBoundingClientRect();
|
||||
const committedScale = rect.width > 0 ? rect.width / width : scale;
|
||||
const next = {
|
||||
width,
|
||||
height,
|
||||
scale: committedScale,
|
||||
originLeft: rect.left,
|
||||
originTop: rect.top,
|
||||
};
|
||||
setViewport((current) =>
|
||||
current.width === next.width &&
|
||||
current.height === next.height &&
|
||||
current.scale === next.scale &&
|
||||
current.originLeft === next.originLeft &&
|
||||
current.originTop === next.originTop
|
||||
? current
|
||||
: next,
|
||||
);
|
||||
}, [height, scale, width]);
|
||||
|
||||
useEffect(() => {
|
||||
const frame = frameRef.current;
|
||||
let animationFrame = requestAnimationFrame(measure);
|
||||
const scheduleMeasure = () => {
|
||||
cancelAnimationFrame(animationFrame);
|
||||
animationFrame = requestAnimationFrame(measure);
|
||||
};
|
||||
const observer =
|
||||
typeof ResizeObserver === "undefined"
|
||||
? null
|
||||
: new ResizeObserver(scheduleMeasure);
|
||||
|
||||
if (frame) observer?.observe(frame);
|
||||
observer?.observe(document.documentElement);
|
||||
window.addEventListener("resize", scheduleMeasure);
|
||||
window.visualViewport?.addEventListener("resize", scheduleMeasure);
|
||||
|
||||
return () => {
|
||||
cancelAnimationFrame(animationFrame);
|
||||
observer?.disconnect();
|
||||
window.removeEventListener("resize", scheduleMeasure);
|
||||
window.visualViewport?.removeEventListener("resize", scheduleMeasure);
|
||||
};
|
||||
}, [measure]);
|
||||
|
||||
const value = useMemo<CourseCanvasContextValue>(
|
||||
() => ({ width, height, scale, portalContainer, viewport }),
|
||||
[height, scale, viewport, width],
|
||||
);
|
||||
|
||||
return (
|
||||
<CanvasContext.Provider value={value}>
|
||||
<div
|
||||
className={className}
|
||||
style={{
|
||||
display: "flex",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
ref={frameRef}
|
||||
style={{
|
||||
position: "relative",
|
||||
width: width * scale,
|
||||
height: height * scale,
|
||||
flex: "none",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={canvasClassName}
|
||||
style={{
|
||||
position: "relative",
|
||||
width,
|
||||
height,
|
||||
transform: `scale(${scale})`,
|
||||
transformOrigin: "left top",
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
<div
|
||||
ref={portalContainer}
|
||||
data-course-portal-root=""
|
||||
className={portalClassName}
|
||||
style={{
|
||||
position: "absolute",
|
||||
inset: 0,
|
||||
pointerEvents: "none",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CanvasContext.Provider>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user