123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274 |
- 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 (
- <>
- <Wrapper
- ref={this.containerRef}
- style={{ height: this.props.height || "500vh" }}
- >
- <div>{this.props.name}</div>
- <div className="loading-wrap" ref={this.loadingWrap}></div>
- <div ref={this.viewerRef}>
- <div style={{ overflow: "hidden" }}>
- <canvas
- css={css`
- width: auto;
- margin-left: 50%;
- transform: translateX(-50%);
- height: calc(var(--vh, 1vh) * 100);
- `}
- ref={this.canvasRef}
- width={this.width}
- height={this.height}
- ></canvas>
- </div>
- </div>
- <>{this.props.children}</>
- </Wrapper>
- </>
- );
- }
- }
|