123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354 |
- import { isMobile } from "./isMoblie";
- import { throttle, debounce, clamp } from "./fn";
- import gsap from "gsap";
- import mitt from "mitt";
- class Mitt {
- constructor(e) {
- Object.assign(this, mitt(e));
- }
- }
- export class CanvasPlayer extends Mitt {
- constructor(canvasId, setting) {
- super();
- this.canvas = null;
- this.canvasId = canvasId;
- this.context = null;
- this.vw = null;
- this.vh = null;
- this.imageW = null;
- this.imageH = null;
- this.setting = setting;
- this.currentFrame = 0; // 当前真正
- this.movingFrame = 0; // 当前前进
- this.scrollFrame = 0;
- this.lastScroll = 0;
- this.isRendering = false;
- this.localRender = [];
- this.currentType = 0;
- this.frames = [];
- this.clips = [];
- this.canScroll = false;
- this.resize = this.resize.bind(this);
- this.watchScroll = this.watchScroll.bind(this);
- }
- init() {
- this.canvas = document.getElementById(this.canvasId);
- this.context = this.canvas.getContext("2d", {
- alpha: true,
- desynchronized: true,
- powerPreference: "high-performance",
- });
- this.vw = this.canvas.width = window.innerWidth;
- this.vh = this.canvas.height = window.innerHeight;
- this.imageW = isMobile() ? 750 : 1920;
- this.imageH = isMobile() ? 1334 : 1080;
- this.proload();
- this.initClipAnimate();
- this.loadEvent();
- }
- loadImage(url) {
- return new Promise((resolve, reject) => {
- const img = new Image(this.imageW, this.imageH);
- img.onload = () => {
- resolve(img);
- };
- img.onerror = (error) => {
- resolve();
- };
- img.src = url;
- });
- }
- proload() {
- if (this.setting) {
- const list = [];
- const total = Array.from(this.setting).reduce(
- (pre, current) => pre + current["total"],
- 0
- );
- Array.from(this.setting).forEach((item, framekey) => {
- const base = [];
- const clip = {
- id: item.sectionType,
- total: item.total,
- x: 0,
- animation: null,
- };
- this.clips.push(clip);
- for (let key = 0; key < item.total; key++) {
- const padLength = item.total.toString().length + 1;
- let imgIndex = String(key).padStart(padLength, "0");
- let url = `${item.imageUrl}/${imgIndex}.webp`;
- const res = this.loadImage(url).then((image) => {
- if (image) {
- const frame = {
- id: item.sectionType,
- index: key,
- image: image,
- total: item.total,
- };
- this.context.drawImage(image, 0, 0, this.vw, this.vh);
- this.frames.push(frame);
- const cu = key + framekey * item.total;
- const process = Math.floor(Number(cu / total) * 100);
- this.emit("updatePress", process);
- }
- });
- list.push(res);
- }
- });
- Promise.all(list).then(() => {
- console.warn("all load");
- this.emit("updatePress", 100);
- this.emit("loaded");
- });
- }
- }
- loadEvent() {
- window.addEventListener("resize", this.resize, false);
- window.addEventListener("wheel", this.watchScroll, false);
- }
- unLoadEvent() {
- window.removeEventListener("resize", this.resize, false);
- window.removeEventListener("wheel", this.watchScroll, false);
- }
- resize() {
- this.vw = this.canvas.width = window.innerWidth;
- this.vh = this.canvas.height = window.innerHeight;
- }
- enableScroll(type = 0) {
- this.canScroll = true;
- this.currentType = type;
- this.initFirstFrame();
- }
- unEnableScroll() {
- this.canScroll = false;
- }
- manualScroll(event, type) {
- if (!this.canScroll) {
- this.enableScroll(type);
- this.initFirstFrame();
- }
- const scrollY = event.target.scrollTop;
- if (scrollY > 0 && this.lastScroll <= scrollY) {
- this.lastScroll = scrollY;
- // console.log("Scrolling DOWN");
- this.toScroll(scrollY, true, event);
- } else {
- this.lastScroll = scrollY;
- // console.log("Scrolling UP");
- this.toScroll(scrollY, false, event);
- }
- // deltaY = scrollY - lastKnownScrollPosition;
- // const deltaHeight = clip.total * 100 - window.innerHeight;
- // const prcess = scrollY / deltaHeight;
- // const frame = Math.round(clip.total * prcess);
- }
- toScroll(scrollY, na, event) {
- let timer, completeTimer;
- const clip = Array.from(this.clips).find(
- (item) => item.id === this.currentType
- );
- // const deltaHeight = clip.total * 100 + window.innerHeight;
- // const prcess = scrollY / deltaHeight;
- // const startFrame = Math.floor(clip.total * prcess);
- const startFrame = this.getframeByHeight(scrollY);
- if (timer) {
- clearTimeout(timer);
- timer = null;
- }
- this.currentFrame = startFrame;
- const matchDis = 10;
- const dynamicDistance = na
- ? this.currentFrame + matchDis
- : this.currentFrame - matchDis;
- // 少于frame
- const dis = clamp(dynamicDistance, 0, clip.total);
- console.log("startFrame", this.currentFrame, dis, this.movingFrame);
- if (this.scrollAni) {
- // console.log("11", this.scrollAni.duration());
- this.scrollAni.kill();
- this.scrollAni = null;
- }
- timer = setTimeout(() => {
- this.scrollAni = gsap.timeline();
- this.isRendering = true;
- this.scrollAni.to(this, {
- movingFrame: dis,
- // ease: "power1.inOut",
- // duration: 0.6,
- yoyo: true,
- onUpdate: () => {
- this.isRendering = true;
- const mFrame = Math.floor(this.movingFrame);
- this.reDraw(mFrame, this.currentType);
- },
- onStart: () => {},
- onComplete: () => {
- completeTimer = setTimeout(this.toRunPatch, 0);
- this.scrollAni && this.scrollAni.pause();
- },
- });
- }, 40);
- }
- watchScroll(event) {
- if (this.canScroll) {
- if (this.currentFrame < 0) {
- this.currentFrame = 0;
- }
- if (event.deltaY > 0) {
- // this.autoIncrement(true);
- } else {
- // this.autoIncrement(false);
- }
- }
- }
- getframeByHeight(height) {
- if (this.currentType) {
- const clip = Array.from(this.clips).find(
- (item) => item.id === this.currentType
- );
- const deltaHeight = clip.total * 100 - window.innerHeight;
- const prcess = height / deltaHeight;
- const frame = Math.round(clip.total * prcess);
- return clamp(frame, 0, clip.total);
- }
- return 0;
- }
- getFrameScrollTop(frame) {
- if (this.currentType) {
- const clip = Array.from(this.clips).find(
- (item) => item.id === this.currentType
- );
- const updateHeight = clamp(frame * 100, 0, clip.total * 100);
- return updateHeight;
- }
- return 0;
- }
- updateScrollTop(frame) {
- if (this.currentType) {
- const bar = document.querySelector(`.scroll-bar-${this.currentType}`);
- const updateHeight = frame * 100 + window.innerHeight;
- console.log("updateScrollTop", updateHeight);
- bar.style.scrollTop = updateHeight;
- }
- }
- initClipScrollheight() {
- if (this.currentType) {
- const bar = document.querySelector(
- `.scroll-bar-${this.currentType}-placeholder`
- );
- const clip = Array.from(this.clips).find(
- (item) => item.id === this.currentType
- );
- const deltaHeight = clip.total * 100;
- bar.style.height = `${deltaHeight}px`;
- }
- }
- resetClipScrollTop() {
- if (this.currentType) {
- const bar = document.querySelector(`.scroll-bar-${this.currentType}`);
- bar.style.scrollTop = 0;
- }
- }
- noticeProcess() {
- const clip = Array.from(this.clips).find(
- (item) => item.id === this.currentType
- );
- const process = Number(this.currentFrame / clip.total) * 100;
- this.emit("process", {
- process,
- type: this.currentType,
- });
- }
- test() {
- const height = this.getFrameScrollTop(this.currentFrame);
- console.log("target-height", height);
- document.querySelector(".scroll-bar-3").scrollTop = height;
- }
- play(frame) {
- console.log("play", frame);
- const height = this.getFrameScrollTop(frame);
- console.log("target-height", height);
- this.lastScroll = height;
- this.currentFrame = frame;
- document.querySelector(".scroll-bar-3").scrollTo({ top: height, left: 0 });
- }
- initClipAnimate() {
- Array.from(this.clips).forEach((item, key) => {
- const duration = this.clips[key].total / 15;
- const anmi = gsap.to(this.clips[key], duration, {
- x: this.clips[0].total - 1,
- repeat: 0,
- duration: (this.clips[key].total / 2) * 1000,
- ease: "none",
- yoyo: true,
- onComplete: () => {
- console.log("done");
- },
- onUpdate: () => {
- const frame = Math.floor(this.clips[key].x);
- this.reDraw(frame, this.currentType);
- },
- });
- anmi.pause();
- this.clips[key].animation = anmi;
- });
- }
- autoPlay() {
- console.log("this.clips[0]", this.clips[0].animation);
- this.clips[0].animation.restart();
- }
- initFirstFrame() {
- if (this.currentType) {
- const frameItem = this.frames.find(
- (item) => item.id == this.currentType && item.index == 1
- );
- this.context.clearRect(0, 0, this.vw, this.vh);
- this.context.drawImage(frameItem.image, 0, 0, this.vw, this.vh);
- this.initClipScrollheight();
- this.scrollAni = gsap.timeline();
- let doneScroll = () => {
- this.isRendering = false;
- if (this.currentFrame === 0) {
- this.emit("scroll-prev");
- }
- if (this.currentFrame === frameItem.total) {
- this.emit("scroll-next");
- }
- console.warn("scroll done", this.currentFrame, this.movingFrame);
- };
- this.toRunPatch = debounce(doneScroll, 400);
- console.log("initFirstFrame");
- }
- }
- reDraw(frame, type) {
- if (isMobile()) {
- } else {
- const frameItem = this.frames.find(
- (item) => item.id == type && item.index == frame
- );
- if (frameItem && frameItem.index <= frameItem.total) {
- this.context.clearRect(0, 0, this.vw, this.vh);
- this.context.drawImage(frameItem.image, 0, 0, this.vw, this.vh);
- }
- }
- }
- update() {}
- }
|