import { Component, createRef } from "react"; import PropTypes from "prop-types"; import { gsap, ScrollTrigger } from "gsap/all"; import styled from "@emotion/styled"; import { css } from "@emotion/react"; export default class Viewer extends Component { constructor() { super(); console.log("hello constructor"); //ref this.containerRef = createRef(null); this.viewerRef = createRef(null); this.canvasContainerRef = createRef(null); this.loadingWrap = createRef(null); this.viewerOffsetRef = createRef(null); this.canvasRef = createRef(); this.sequence = []; this.loadedRenderPool = []; this.enterTimeline = false; this.exitTimeline = false; this.loadComplete = false; this.loadedCount = 0; this.progress = 0; this.lastFrame = -1; this.floatFrame = 0; this.loadedRenderTimeout = null; this.poolAnimateDelay = 40; this.context = null; this.width = 1552; this.height = 873; this.justScrolled = false; this.isBelow = true; this.isAbove = false; } static propTypes = { name: PropTypes.string, height: PropTypes.string, path: PropTypes.string, frameCount: PropTypes.number, enterTween: PropTypes.func, exitTween: PropTypes.func, canvasSize: PropTypes.array, pause: PropTypes.object, children: PropTypes.object, }; state = {}; componentWillUnmount() { this.timeline && this.timeline.kill(true); } componentDidMount() { this.loadAssets(); this.canvasRef.current && this.initializeCanvas(); if (!this.timeline) { this.initializeTimeline(); this.setTimeline(); } if (!this.enterTimeline && this.props.enterTween) { this.initializeEnterTween(); } ScrollTrigger.refresh(); } loadImage(index) { const img = new Image(); // console.log("index", this.getSourcePath(index)); img.retried = 0; img.src = this.getSourcePath(index); img.ogSrc = img.src; if (this.props.pause && index + "" in this.props.pause) { for (var r = this.props.pause[index]; r--; ) { this.sequence.push(img); } } this.sequence.push(img); img.onload = () => { index === 1 && this.renderImageToCanvas(0); if (this.frame > index && this.timeline.scrollTrigger.isActive) { this.poolNewFrames(index - 1); } this.loadedCount += 1; if (this.loadedCount === parseFloat(this.props.frameCount) - 1) { this.loadingComplete(); } }; } getSourcePath(index) { const defaultPrefix = import.meta.env.VITE_APP_SOURCE; return `${defaultPrefix}${this.props.path}/${"".concat( index.toString().padStart(4, "0") )}.webp`; } loadAssets() { this.loadImage(1); setTimeout(() => { for (var t = 2; t <= this.props.frameCount; t += 1) { this.loadImage(t); } }, 60); } loadingComplete() { console.log(this.props.path, "loading complete"); this.loadComplete = true; this.isAbove && this.renderImageToCanvas(this.loadedCount - 1); } initializeCanvas() { this.context = this.canvasRef.current.getContext("2d", { alpha: false, desynchronized: true, powerPreference: "high-performance", }); this.context.imageSmoothingEnabled = true; this.context.imageSmoothingQuality = "high"; } initializeTimeline() { const openLoading = () => { gsap.to(this.loadingWrap.current, { autoAlpha: 1, }); }; const closeLoading = () => { gsap.to(this.loadingWrap.current, { autoAlpha: 0, }); }; closeLoading(); this.timeline = gsap.timeline({ scrollTrigger: { trigger: this.containerRef.current, pin: this.viewerRef.current, scrub: 0.66, start: "top top", end: "bottom bottom", ease: "none", markers: true, onUpdate: function (n) { //处理processloading }, onScrubComplete: () => { this.justScrolled = true; }, onEnter: () => { closeLoading(); this.isAbove = false; console.log("onEnter"); }, onEnterBack: () => { openLoading(); console.log("onEnterBack"); this.isBelow = false; }, onLeave: () => { console.log("onLeave"); this.isAbove = true; openLoading(); }, onLeaveBack: () => { console.log("onLeaveBack"); openLoading(); this.isBelow = true; }, }, }); } setTimeline() { console.log("this.fullFrameCount", this.fullFrameCount); this.timeline.to(this, { floatFrame: this.fullFrameCount - 1, ease: "none", onUpdate: () => { this.frame = Math.floor(this.floatFrame); if (this.lastFrame === this.frame || this.loadedRenderPool.length > 0) { this.renderImageToCanvas(this.frame); } }, }); } initializeEnterTween() { console.log("initializeEnterTween"); } poolNewFrames(index) { console.log("poolNewFrames", index); this.loadedRenderPool.unshift(index); this.loadedRenderPool.sort(function (e, t) { return t - e; }); this.animatePool(); } animatePool() { if (!this.loadedRenderTimeout && this.loadedRenderPool.length) { this.loadedRenderTimeout = setTimeout(() => { this.loadedRenderTimeout = true; var poolFrame = this.loadedRenderPool[this.loadedRenderPool.length - 1]; if (poolFrame <= this.frame) { var remainFrame = this.loadedRenderPool.pop(); this.renderImageToCanvas(remainFrame); this.animatePool(); } if (this.frame < poolFrame) { this.loadedRenderPool = []; } }, this.poolAnimateDelay); } } renderImageToCanvas(index) { if (this.sequence[index]) { if (this.context.drawImage) { console.log("renderImageToCanvas", index); this.context.drawImage(this.sequence[index], 0, 0); this.lastFrame = index; } else { this.initializeCanvas(); } } } render() { // process props this.fullFrameCount = this.props.frameCount; if (this.props.pause) { Object.keys(this.props.pause).forEach((index) => { this.fullFrameCount += this.props.pause[index]; }); } const Wrapper = styled.div({ position: "relative", margin: "auto", textAlign: "center", pointerEvents: "none", maxWidth: "100vw", }); return ( <>
{this.props.name}
<>{this.props.children}
); } }