如何自定制audio音频播放器的风格。

这里列举一个简单的测试:对比 PNG 原图、PNG 无损压缩、PNG 转 WebP(无损)、PNG 转 WebP(有损)的压缩效果。更多测试查看这里
webp的最大优势是使图片的体积小,同时具备了有损和无损的压缩模式,支持透明度和动画等特性。
webp在兼容性方面 兼容性查看
目前在pc端,在chrome和firefox浏览器没啥问题。移动端的问题较大,在安卓4.0原生系统以上支持,在ios系统目前不适用。
<figure class="img js-img u-img-placeholder" data-echo="${imgsrc}"></figure>
const lazyLoader = () => {
  const CLIENT_HEIGHT = document.documentElement.clientHeight
  let imgs
  function init() {
    document.removeEventListener('scroll', load, false)
    document.addEventListener('scroll', load, {
      passive: true
    })
    const currentTop = document.body.scrollTop
    imgs = [].slice.call(document.querySelectorAll('[data-echo]:not(.img-error):not(.img-loading)')).map((item) => {
      return {
        el: item,
        top: item.getBoundingClientRect().top + currentTop
      }
    }).sort((a, b) => a.top - b.top)
    load()
  }
  function load() {
    const currentTop = document.body.scrollTop
    let index = 0
    for (let i = 0; i < imgs.length; i++) {
      const el = imgs[i].el
      if (imgs[i].top >= currentTop + (CLIENT_HEIGHT * 0.9)) {
        index = i
        break
      } else if (el.dataset.echo) {
        insertImg(el)
      }
    }
    imgs = imgs.slice(index)
    if (imgs.length === 0) {
      document.removeEventListener('scroll', load, false)
    }
  }
  function cssSupports(property) {
    if (!window.CSS || !window.CSS.supports) {
      return false
    }
    return CSS.supports(property)
  }
  function createImg({ src, alt }, onload = () => {}, onerror = () => {}) {
    const img = new Image()
    img.style.opacity = 0
    const prop = cssSupports('transition') ? 'transition' : 'webkitTransition'
    img.style[prop] = 'opacity .2s ease-in-out'
    img.src = src
    img.alt = alt || ''
    img.onload = () => {
      // setTimeout(onload, 0)
      onload()
      img.style.opacity = 1
      setTimeout(() => {
        img.style.cssText = ''
      }, 200)
    }
    img.onerror = () => {
      onerror && onerror(img)
    }
    return img
  }
  function insertImg(el, onload = () => {}, onerror = () => {}) {
    /* eslint-disable no-param-reassign */
    const dataset = el.dataset
    const img = createImg({ src: dataset.echo, alt: dataset.alt }, () => {
      el.classList.add('img-loaded')
      el.classList.remove('u-img-placeholder')
      el.classList.remove('img-error')
      el.classList.remove('img-loading')
      el.dataset.echo = ''
      onload()
    }, (img) => {
      el.classList.add('img-error')
      el.classList.remove('img-loading')
      el.removeChild(img)
      if (el.dataset.retry) {
        el.dataset.text = '点击加载'
        el.addEventListener('click', retryHandler, false)
      }
      onerror()
    })
    if (!el.classList.contains('img-loading')) {
      el.classList.add('img-loading')
      el.insertBefore(img, el.firstChild)
    }
    /* eslint-enable no-param-reassign */
  }
  function retryHandler(event) {
    event.preventDefault()
    event.stopImmediatePropagation()
    const target = event.target
    target.dataset.text = '加载中'
    insertImg(target, () => {
      target.dataset.text = ''
      target.dataset.retry = ''
    }, () => {
      target.dataset.text = '点击加载'
    }) 
    target.removeEventListener('click', retryHandler, false)
  }
  function refresh() {
    init()
  }
  return {
    init,
    refresh
  }
}
export default lazyLoader()
nodejs使用了一个事件驱动,非阻塞I/O模式,使其轻量又高效。
浏览器窗口有一个history对象,用来保存浏览历史。
最常用的属性:length
history.length;
history是不可遍历的.