HTML Canvas 未在 Safari 上显示

Ant*_*nta 6 html scroll canvas

我正在使用下面 Codepen 中的代码来创建一个全角画布,用于模拟滚动图像序列上的视频播放。

\n

它在 Chrome 中工作正常,但我无法使其在 Safari \xe2\x80\x93 上工作,它只是显示为白色,就好像它是空的一样。如果我检查该元素,它似乎不存在于 Safari 中。

\n

是否存在已知问题或可以在代码中修复的问题?

\n

代码笔: https: //codepen.io/SalmanShaikh/pen/MWeNBLL

\n
const appleSequenceImages = [];\n//number of images 131; fill the array\n//see the squence obj below for image path.\nfor (let i = 0; i <= 374; i++) {\n  console.log(`${`${i}`.slice(-4)}.jpg`);\n  appleSequenceImages.push(`${`${i}`.slice(-4)}.jpg`);\n}\n\n\nconst requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;\n\nclass EventEmitter {\n  listeners = {}\n  addListener(eventName, fn) {\n    this.listeners[eventName] = this.listeners[eventName] || [];\n    this.listeners[eventName].push(fn);\n    return this;\n  }\n  on(eventName, fn) {\n    return this.addListener(eventName, fn);\n  }\n  once(eventName, fn) {\n    this.listeners[eventName] = this.listeners[eventName] || [];\n    const onceWrapper = () => {\n      fn();\n      this.off(eventName, onceWrapper);\n    }\n    this.listeners[eventName].push(onceWrapper);\n    return this;\n  }\n  off(eventName, fn) {\n    return this.removeListener(eventName, fn);\n  }\n  removeListener(eventName, fn) {\n    let lis = this.listeners[eventName];\n    if (!lis) return this;\n    for (let i = lis.length; i > 0; i--) {\n      if (lis[i] === fn) {\n        lis.splice(i, 1);\n        break;\n      }\n    }\n    return this;\n  }\n  emit(eventName, ...args) {\n    let fns = this.listeners[eventName];\n    if (!fns) return false;\n    fns.forEach((f) => {\n      f(...args);\n    });\n    return true;\n  }\n  listenerCount(eventName) {\n    let fns = this.listeners[eventName] || [];\n    return fns.length;\n  }\n  rawListeners(eventName) {\n    return this.listeners[eventName];\n  }\n}\nclass Canvas {\n  constructor(e) {\n    this.images = e.images;\n    this.container = e.container;\n    this.cover = e.cover;\n    this.displayIndex = 0;\n  }\n\n  setup() {\n    this.canvas = document.createElement("canvas");\n    this.container.appendChild(this.canvas);\n    this.ctx = this.canvas.getContext(\'2d\')\n\n    window.addEventListener(\'resize\', () => this.resize());\n    this.resize();\n  }\n\n  renderIndex(e) {\n    if (this.images[e]) {\n      return this.drawImage(e);\n    }\n    // Find closest loaded image\n    for (var t = Number.MAX_SAFE_INTEGER, r = e; r >= 0; r--)\n      if (this.images[r]) {\n        t = r;\n        break\n      }\n    for (var n = Number.MAX_SAFE_INTEGER, i = e, o = this.images.length; i < o; i++)\n      if (this.images[i]) {\n        n = i;\n        break\n      }\n    this.images[t] ? this.drawImage(t) : this.images[n] && this.drawImage(n)\n  }\n\n  drawImage(e) {\n    this.displayIndex = e,\n      this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\n    const x = Math.floor((this.canvas.width - this.images[this.displayIndex].naturalWidth) / 2);\n    const y = Math.floor((this.canvas.height - this.images[this.displayIndex].naturalHeight) / 2);\n    if (this.cover) {\n\n      this.drawImageCover(this.ctx, this.images[this.displayIndex]);\n    } else {\n      this.ctx.drawImage(this.images[this.displayIndex], x, y);\n    }\n  }\n\n  resize() {\n    const w = this.container.clientWidth;\n    const h = this.container.clientHeight;\n    this.canvas.style.height = `${h}px`;\n    this.canvas.style.width = `${w}px`;\n    this.canvas.height = h;\n    this.canvas.width = w;\n\n    this.renderIndex(this.displayIndex);\n  }\n\n  /**\n   * Main code by Ken Fyrstenberg Nilsen, modified by me.\n   *\n   * drawImageProp(context, image [, x, y, width, height [,offsetX, offsetY]])\n   *\n   * If image and context are only arguments rectangle will equal canvas\n   */\n  drawImageCover(ctx, img, x, y, w, h, offsetX, offsetY) {\n\n    if (arguments.length === 2) {\n      x = y = 0;\n      w = ctx.canvas.width;\n      h = ctx.canvas.height;\n    }\n\n    // default offset is center\n    offsetX = typeof offsetX === "number" ? offsetX : 0.5;\n    offsetY = typeof offsetY === "number" ? offsetY : 0.5;\n\n    // keep bounds [0.0, 1.0]\n    if (offsetX < 0) offsetX = 0;\n    if (offsetY < 0) offsetY = 0;\n    if (offsetX > 1) offsetX = 1;\n    if (offsetY > 1) offsetY = 1;\n    //added new prop.width and height for precise movement\n    var iw = img.width,\n      ih = img.height,\n      r = Math.min(w / iw, h / ih),\n      nw = iw * r, // new prop. width\n      nh = ih * r, // new prop. height\n      cx, cy, cw, ch, ar = 1;\n\n    // decide which gap to fill\n    if (nw < w) ar = w / nw;\n    if (Math.abs(ar - 1) < 1e-14 && nh < h) ar = h / nh; // updated\n    nw *= ar;\n    nh *= ar;\n\n    // calc source rectangle\n    cw = iw / (nw / w);\n    ch = ih / (nh / h);\n\n    cx = (iw - cw) * offsetX;\n    cy = (ih - ch) * offsetY;\n\n    // make sure source rectangle is valid\n    if (cx < 0) cx = 0;\n    if (cy < 0) cy = 0;\n    if (cw > iw) cw = iw;\n    if (ch > ih) ch = ih;\n\n    // fill image in dest. rectangle\n    ctx.drawImage(img, cx, cy, cw, ch, x, y, w, h);\n  }\n}\nclass ImgLoader extends EventEmitter {\n  constructor(opts) {\n    super();\n    this.images = opts.imgsRef;\n    this.imageNames = opts.images;\n    this.imagesRoot = opts.imagesRoot;\n    this.sequenceLength = opts.images.length;\n    this.priorityFranes = opts.priorityFrames;\n    this.complete = false;\n    this.loadIndex = 0;\n\n    this.priorityQueue = this.createPriorityQueue();\n    this.loadingQueue = this.createLoadingQueue();\n\n    this.loadNextImage();\n  }\n\n  loadImage(e) {\n    if (this.images[e]) {\n      return this.loadNextImage();\n    }\n    const onLoad = () => {\n      img.removeEventListener(\'load\', onLoad);\n      this.images[e] = img;\n\n      if (e === 0) {\n        this.emit(\'FIRST_IMAGE_LOADED\');\n      }\n      this.loadNextImage();\n    }\n    const img = new Image;\n    img.addEventListener(\'load\', onLoad);\n    img.src = (this.imagesRoot ? this.imagesRoot : \'\') + this.imageNames[e];\n  }\n\n  loadNextImage() {\n    if (this.priorityQueue.length) {\n      this.loadImage(this.priorityQueue.shift());\n      if (!this.priorityQueue.length) {\n        this.emit(\'PRIORITY_IMAGES_LOADED\');\n      }\n    } else if (this.loadingQueue.length) {\n      this.loadImage(this.loadingQueue.shift())\n    } else {\n      this.complete = true;\n      this.emit(\'IMAGES_LOADED\');\n    }\n  }\n\n  createPriorityQueue() {\n    const p = this.priorityFrames || [];\n    if (!p.length) {\n      p.push(0);\n      p.push(Math.round(this.sequenceLength / 2));\n      p.push(this.sequenceLength - 1);\n    }\n    return p;\n  }\n\n  createLoadingQueue() {\n    return this.imageNames.map((s, i) => i).sort((e, n) => {\n      return Math.abs(e - this.sequenceLength / 2) - Math.abs(n - this.sequenceLength / 2)\n    });\n  }\n}\nclass ScrollSequence {\n  constructor(opts) {\n    this.opts = {\n      container: \'body\',\n      starts: \'out\',\n      ends: \'out\',\n      imagesRoot: \'\',\n      cover: false,\n      ...opts\n    }\n    this.container = typeof opts.container === \'object\' ?\n      opts.container :\n      document.querySelector(opts.container);\n\n    this.scrollWith = !opts.scrollWith ?\n      this.container :\n      typeof opts.scrollWith === \'object\' ?\n      opts.scrollWith :\n      document.querySelector(opts.scrollWith);\n\n    this.images = Array(opts.images.length);\n    this.imagesToLoad = opts.images;\n    this.priorityFrames = opts.priorityFrames;\n\n    this.loader = new ImgLoader({\n      imgsRef: this.images,\n      images: this.imagesToLoad,\n      imagesRoot: this.opts.imagesRoot,\n      priorityFrames: this.priorityFrames\n    });\n\n    this.canvas = new Canvas({\n      container: this.container,\n      images: this.images,\n      cover: this.opts.cover\n    });\n\n    this.init();\n  }\n\n  init() {\n    this.canvas.setup();\n    this.loader.once(\'FIRST_IMAGE_LOADED\', () => {\n      this.canvas.renderIndex(0);\n    })\n    this.loader.once(\'PRIORITY_IMAGES_LOADED\', () => {\n      window.addEventListener(\'scroll\', () => this.changeOnWindowScroll());\n    })\n    this.loader.once(\'IMAGES_LOADED\', () => {\n      console.log(\'Sequence Loaded\');\n    })\n  }\n\n  changeOnWindowScroll() {\n    const step = 100 / (this.images.length - 1);\n    const mapToIndex = Math.floor(this.percentScrolled / step);\n    requestAnimationFrame(() => this.canvas.renderIndex(mapToIndex));\n  }\n\n  get percentScrolled() {\n    const {\n      starts,\n      ends\n    } = this.opts;\n    const el = this.scrollWith;\n    const doc = document.documentElement;\n    const clientOffsety = doc.scrollTop || window.pageYOffset;\n    const elementHeight = el.clientHeight || el.offsetHeight;\n    const clientHeight = doc.clientHeight;\n    let target = el;\n    let offsetY = 0;\n    do {\n      offsetY += target.offsetTop;\n      target = target.offsetParent;\n    } while (target && target !== window);\n\n    let u = (clientOffsety - offsetY);\n    let d = (elementHeight + clientHeight)\n\n    if (starts === \'out\') u += clientHeight;\n    if (ends === \'in\') d -= clientHeight;\n    if (starts == \'in\') d -= clientHeight;\n\n    const value = u / d * 100;\n    return value > 100 ? 100 : value < 0 ? 0 : value;\n  }\n}\n\nconst appleSequence = new ScrollSequence({\ncontainer: \'.apple-sequence\',\nscrollWith: \'.apple-container\',\nimages: appleSequenceImages,\nimagesRoot: \'https://www.apple.com/105/media/us/airpods-pro/2019/1299e2f5_9206_4470_b28e_08307a42f19b/anim/sequence/large/02-head-bob-turn/\',\npriorityFrames: [0],\ncover: true,\nplayUntil: \'scroll-out\',\nstarts: \'in\'\n});\n\n// END SCROLL_SEQUENCE CODE\n\n});\n
Run Code Online (Sandbox Code Playgroud)\n

小智 0

我刚刚在 Safari 上测试了你的代码,它对我来说工作得很好。我什至在控制台中检查过,没有任何错误,只是加载了图像名称。

尝试清理缓存并再次检查。如果您得到相同的结果,请尝试使用 Safari 预览

使用的 Safari 版本:14.1.2

  • 这并没有提供问题的答案。一旦您拥有足够的[声誉](https://stackoverflow.com/help/whats-reputation),您将能够[对任何帖子发表评论](https://stackoverflow.com/help/privileges/comment);相反,[提供不需要询问者澄清的答案](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-c​​an-我-做-相反)。- [来自评论](/review/late-answers/30009424) (2认同)