import {
Component,
createRef,
Children,
isValidElement,
cloneElement,
} from "react";
import PropTypes from "prop-types";
import { gsap, ScrollTrigger } from "gsap/all";
import LazyLoad from "react-lazyload";
import { css } from "@emotion/react";
// import { Style } from "../style/viewer";
export default function Viewer(props) {
const lazyHeight = 0.01 * parseFloat(props.height) * window.innerHeight;
// offset={1e4}
var allChildren = Children.map(props.children, function (element) {
return isValidElement(element)
? cloneElement(element, {
parentHeight: props.height,
debug: props.debug,
})
: element;
});
const debug = props.debug || false;
// console.log("lazyHeight", lazyHeight);
return (
{allChildren}
);
}
Viewer.propTypes = {
name: PropTypes.string,
height: PropTypes.string,
path: PropTypes.string,
frameCount: PropTypes.number,
startFrame: PropTypes.number,
enterTween: PropTypes.object,
exitTween: PropTypes.object,
canvasSize: PropTypes.array,
pause: PropTypes.object,
children: PropTypes.any,
debug: PropTypes.bool,
scrollSpeed: PropTypes.number,
};
class ViewerInner extends Component {
constructor(props) {
super();
//ref
this.containerRef = createRef(null);
this.viewerRef = createRef(null);
this.canvasContainerRef = createRef(null);
this.loadingWrap = createRef(null);
this.viewerOffsetRef = createRef(null);
this.canvasRef = createRef(null);
this.processingRef = createRef(null);
this.preProcessingRef = createRef(null);
this.processBarRef = createRef(null);
this.sequence = [];
this.loadedRenderPool = [];
this.enterTimeline = false;
this.exitTimeline = false;
this.loadComplete = false;
this.playBarTween = false;
this.playPreBarTween = false;
this.loadedCount = 0;
this.progress = 0;
this.lastFrame = -1;
this.floatFrame = 0;
this.frame = 0;
this.loadedRenderTimeout = null;
this.poolAnimateDelay = 40;
this.context = null;
this.width = 1552;
this.height = 873;
this.justScrolled = false;
this.lastProgress = false;
this.isBelow = true;
this.isAbove = false;
props.debug && console.log("init ", props.name);
}
static propTypes = Viewer.propTypes;
componentWillUnmount() {
this.props.debug && console.warn("remove-timeline");
if (this.timeline) {
this.timeline.kill(true);
}
}
componentDidUpdate() {
if (this.props.debug) {
this.timeline.scrollTrigger.refresh();
}
}
componentDidMount() {
this.fullFrameCount = this.props.frameCount;
this.frame = this.props.startFrame || 0;
if (this.props.pause) {
Object.keys(this.props.pause).forEach((index) => {
this.fullFrameCount += this.props.pause[index];
});
}
this.loadAssets();
this.canvasRef.current && this.initializeCanvas();
if (!this.timeline) {
this.initializeTimeline();
this.setTimeline();
}
if (!this.enterTimeline && this.props.enterTween) {
this.initializeEnterTween();
}
if (!this.exitTimeline && this.props.exitTween) {
this.initializeExitTween();
}
ScrollTrigger.refresh();
}
loadImage(index) {
const img = new Image();
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.onerror = () => {
var timeStamp = Math.floor(Date.now() * Math.random())
.toString()
.substring(0, 8);
img.retried < 2
? setTimeout(() => {
img.src = img.ogSrc + "?" + timeStamp;
}, 80)
: img.retried < 3 &&
setTimeout(() => {
img.src = img.ogSrc.slice(0, -4) + ".jpg?" + timeStamp;
}, 80),
img.retried++;
this.props.debug && console.log("img.retried", img.retried);
};
img.onload = () => {
index === 1 && this.renderImageToCanvas(0);
if (
this.frame > index &&
this.timeline &&
this.timeline.scrollTrigger.isActive
) {
this.poolNewFrames(index - 1);
}
var t = 100 - (this.frame / this.fullFrameCount) * 100 + "%";
if (this.preProcessingRef.current) {
this.preProcessingRef.current.style.width = t;
}
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: this.props.debug || false,
onUpdate: (trigger) => {
//处理processloading
if (!this.lastProgress) {
this.lastProgress = trigger.progress;
} else {
if (this.lastProgress !== this.progress) {
this.justScrolled = true;
this.lastProgress = trigger.progress;
}
}
},
onScrubComplete: () => {
this.justScrolled = true;
},
onEnter: () => {
this.isAbove = false;
console.log(this.props.path, "onEnter");
this.enterShowElements();
},
onEnterBack: () => {
// openLoading();
console.log(this.props.path, "onEnterBack");
this.isBelow = false;
this.enterShowElements();
},
onLeave: () => {
console.log(this.props.path, "onLeave");
this.isAbove = true;
this.leaveHideElements();
},
onLeaveBack: () => {
console.log(this.props.path, "onLeaveBack");
this.isBelow = true;
this.leaveHideElements();
},
},
});
}
setTimeline() {
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() {
const duration = this.props.enterTween.duration || 1;
let openPin = false;
// console.warn("this.props.enterTween", duration, this.props.enterTween);
gsap.set(this.viewerRef.current, {
yPercent: -100 * duration,
});
if (void 0 !== this.props.enterTween.pin) {
openPin = this.props.enterTween.pin;
}
this.enterTimeline = gsap.timeline({
scrollTrigger: {
trigger: this.viewerRef.current,
scrub: true,
pin: openPin,
start: function () {
return "top top";
},
end: function () {
return "top top-=" + window.innerHeight * duration;
},
},
});
if (this.props.enterTween.to) {
this.enterTimeline.to(
this.viewerRef.current,
Object.assign(
{
ease: "none",
},
this.props.enterTween.to
)
);
}
if (this.props.enterTween.from) {
this.enterTimeline.from(
this.viewerRef.current,
Object.assign(
{
ease: "none",
},
this.props.enterTween.from
)
);
}
}
initializeExitTween() {
console.log(this.props.path, "initializing exit tween ");
this.exitTimeline = gsap.timeline({
scrollTrigger: {
scrub: true,
trigger: this.containerRef.current,
pin: this.viewerRef.current,
onLeave: this.props.exitTween.onLeave,
onLeaveBack: this.props.exitTween.onLeaveBack,
onEnterBack: this.props.exitTween.onEnterBack,
start: function () {
return "bottom bottom";
},
end: function () {
return "bottom top";
},
},
});
if (this.props.exitTween.from) {
this.exitTimeline.from(
this.viewerRef.current,
Object.assign(
{
ease: "none",
},
this.props.exitTween.from
)
);
}
if (this.props.exitTween.to) {
this.exitTimeline.to(
this.viewerRef.current,
Object.assign(
{
ease: "none",
},
this.props.exitTween.to
)
);
}
}
poolNewFrames(index) {
this.loadedRenderPool.unshift(index);
this.loadedRenderPool.sort(function (a, b) {
return b - a;
});
this.animatePool();
}
animatePool() {
if (!this.loadedRenderTimeout && this.loadedRenderPool.length) {
this.loadedRenderTimeout = setTimeout(() => {
this.loadedRenderTimeout = false;
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) {
this.context.drawImage(this.sequence[index], 0, 0);
this.lastFrame = index;
this.handleSyncProessBar(index);
} else {
this.initializeCanvas();
}
}
}
handleSyncProessBar(index) {
const progressingPreload =
100 - (this.frame / this.fullFrameCount) * 100 + "%";
const progressing = 100 - (index / this.fullFrameCount) * 100 + "%";
// console.log("handleSyncProessBar", this.processingRef.current);
if (this.preProcessingRef.current) {
this.playPreBarTween = gsap.to(this.preProcessingRef.current, {
duration: 0.05,
right: progressingPreload,
ease: "none",
});
this.playBarTween = gsap.to(this.processingRef.current, {
duration: 0.05,
right: progressing,
ease: "none",
});
}
}
enterShowElements() {
gsap.set(this.processBarRef.current, {
autoAlpha: 1,
});
}
leaveHideElements() {
gsap.set(this.processBarRef.current, {
autoAlpha: 0,
});
}
render() {
return (
<>
>
);
}
}