Robot Img

介绍

一个高效、灵活的图片 Vue 组件,自动实现图片懒加载功能,并能按需适配云厂商的图片处理功能。

npm 安装:

~ npm install @robot-img/vue-img

推荐设置

使用不同云厂商的图片处理功能按需使用图片,已有图片处理函数参考:

也可以通过 createSrcTplFactory 快速创建一个全局图片处理函数

import './index.css'

import { createApp, defineComponent, provide } from 'vue'

import {
  checkWebpSupported,
  createImgPool,
  createSrcTplFactory,
  createSrcTplOfAliOss,
  createSrcTplOfKSYunKS3,
  createSrcTplOfTencent,
  ImgDiv,
} from '@robot-img/vue-img'

async function main() {
  // 判断浏览器是否支持 webp 格式图片
  const webp = await checkWebpSupported()

  // 阿里云,参考:https://help.aliyun.com/document_detail/44688.html
  const imgPoolAliOss = createImgPool({
    createSrcTpl: createSrcTplOfAliOss({
      webp,
    }),
    globalVars: {
      className: 'cloud-img',
    },
  })
  // 可以用这个方法加一个默认样式,当然自行增加也是 OK 的
  /**
   * 根据 globalVars.className 设置一个全局默认样式
   * 默认样式为:
   * ```
   * .${globalVars.className} {
   *  transition: background-image .3s;
   *  background-size: cover;
   *  background-position: center;
   *  background-repeat: no-repeat;
   * }
   * span.${globalVars.className} {
   *  display: inline-block;
   * }
   * ```
   */
  imgPoolAliOss.appendDefaultStyle()

  const ExampleAliOss = defineComponent({
    components: {
      'robot-img': ImgDiv,
    },
    setup() {
      provide('imgPool', imgPoolAliOss)
      return () => <robot-img src="//image-demo.oss-cn-hangzhou.aliyuncs.com/example.jpg" />
    },
  })
  // 金山云,详见:https://docs.ksyun.com/documents/886
  const imgPoolK3S = createImgPool({
    createSrcTpl: createSrcTplOfKSYunKS3({
      webp,
    }),
    globalVars: {
      className: 'cloud-img',
    },
  })
  const ExampleK3S = defineComponent({
    components: {
      'robot-img': ImgDiv,
    },
    setup() {
      provide('imgPool', imgPoolK3S)
      return () => <robot-img src="//ks3-cn-beijing.ksyun.com/ks3-resources/suiyi.jpg" />
    },
  })
  // 腾讯云,详见:https://cloud.tencent.com/document/product/460/36541
  const imgPoolTencent = createImgPool({
    createSrcTpl: createSrcTplOfTencent({
      webp,
    }),
    globalVars: {
      className: 'cloud-img',
    },
  })
  const ExampleTencent = defineComponent({
    components: {
      'robot-img': ImgDiv,
    },
    setup() {
      provide('imgPool', imgPoolTencent)
      return () => (
        <robot-img src="//examples-1251000004.cos.ap-shanghai.myqcloud.com/sample.jpeg" />
      )
    },
  })
  // 自定义,以腾讯云为基础,比如自定义 webp 格式的质量
  const createCustomSrcTpl = createSrcTplFactory((globalVars) => ({ rect, src }) => {
    const configs: string[] = []
    if (rect.width && rect.height) {
      const w = Math.floor(globalVars.ratio * rect.width)
      const h = Math.floor(globalVars.ratio * rect.height)
      configs.push(`1/w/${w}/h/${h}`)
    }
    if (globalVars.webp) {
      configs.push('format/webp/quality/90')
    }
    if (configs.length < 1) {
      return src
    }

    return `${src}?imageView2/${configs.join('/')}`
  })
  const imgPoolCustom = createImgPool({
    createSrcTpl: createCustomSrcTpl({
      webp,
    }),
    globalVars: {
      className: 'cloud-img',
    },
  })

  const ExampleCustom = defineComponent({
    components: {
      'robot-img': ImgDiv,
    },
    setup() {
      provide('imgPool', imgPoolCustom)
      return () => (
        <robot-img src="//examples-1251000004.cos.ap-shanghai.myqcloud.com/sample.jpeg" />
      )
    },
  })

  const App = defineComponent({
    components: {
      'example-ali-oss': ExampleAliOss,
      'example-k3s': ExampleK3S,
      'example-tencent': ExampleTencent,
      'example-custom': ExampleCustom,
    },
    render() {
      return (
        <div>
          <p>阿里云:</p>
          <example-ali-oss />
          <p>金山云:</p>
          <example-k3s />
          <p>腾讯云:</p>
          <example-tencent />
          <p>自定义:</p>
          <example-custom />
        </div>
      )
    },
  })
  createApp(App).mount(document.getElementById('10-recommend')!)
}

main()

默认设置

当不进行任何设置时,组件将只有懒加载功能

import { createApp, defineComponent } from 'vue'

import { Img } from '@robot-img/vue-img'

const App = defineComponent({
  components: {
    'robot-img': Img,
  },
  setup() {
    return () => (
      <robot-img width="150" src="//image-demo.oss-cn-hangzhou.aliyuncs.com/example.jpg" />
    )
  },
})
createApp(App).mount(document.getElementById('20-default')!)

自定义模板

import './index.css'

import { createApp, defineComponent, provide } from 'vue'

import {
  checkWebpSupported,
  createImgPool,
  createSrcTplOfAliOss,
  ImgDiv,
  ImgSrcTplPropFn,
} from '@robot-img/vue-img'

async function main() {
  // 判断浏览器是否支持 webp 格式图片
  const webp = await checkWebpSupported()

  // 阿里云,参考:https://help.aliyun.com/document_detail/44688.html
  const imgPoolAliOss = createImgPool({
    createSrcTpl: createSrcTplOfAliOss({
      webp,
    }),
    globalVars: {
      className: 'tpl-img',
    },
  })
  imgPoolAliOss.appendDefaultStyle()

  const App = defineComponent({
    components: {
      'robot-img': ImgDiv,
    },
    setup() {
      provide('imgPool', imgPoolAliOss)
      const srcTpl: ImgSrcTplPropFn = ({ src, ratioWidth, ratioHeight, webp }) =>
        `${src}?x-oss-process=image/resize,m_lfit,w_${ratioWidth},h_${ratioHeight}${
          webp ? '/format,webp' : ''
        }/blur,r_3,s_2`
      return () => (
        <robot-img srcTpl={srcTpl} src="//image-demo.oss-cn-hangzhou.aliyuncs.com/example.jpg" />
      )
    },
  })
  createApp(App).mount(document.getElementById('40-tpl')!)
}

main()

自定义容器

import './index.css'

import { createApp, defineComponent } from 'vue'

import { checkWebpSupported, createSrcTplOfAliOss, ImgContainer, ImgDiv } from '@robot-img/vue-img'

async function main() {
  // 判断浏览器是否支持 webp 格式图片
  const webp = await checkWebpSupported()

  const App = defineComponent({
    components: {
      'robot-img': ImgDiv,
      container: ImgContainer,
    },
    setup() {
      const createSrcTpl = createSrcTplOfAliOss({
        webp,
      })
      const globalVars = {
        className: 'container-img',
      }
      return () => (
        <container class="demo-container" createSrcTpl={createSrcTpl} globalVars={globalVars}>
          <robot-img src="//image-demo.oss-cn-hangzhou.aliyuncs.com/example.jpg" />
          <robot-img src="//image-demo.oss-cn-hangzhou.aliyuncs.com/example.jpg" />
          <robot-img src="//image-demo.oss-cn-hangzhou.aliyuncs.com/example.jpg" />
          <robot-img src="//image-demo.oss-cn-hangzhou.aliyuncs.com/example.jpg" />
          <robot-img src="//image-demo.oss-cn-hangzhou.aliyuncs.com/example.jpg" />
          <robot-img src="//image-demo.oss-cn-hangzhou.aliyuncs.com/example.jpg" />
          <robot-img src="//image-demo.oss-cn-hangzhou.aliyuncs.com/example.jpg" />
          <robot-img src="//image-demo.oss-cn-hangzhou.aliyuncs.com/example.jpg" />
          <robot-img src="//image-demo.oss-cn-hangzhou.aliyuncs.com/example.jpg" />
          <robot-img src="//image-demo.oss-cn-hangzhou.aliyuncs.com/example.jpg" />
          <robot-img src="//image-demo.oss-cn-hangzhou.aliyuncs.com/example.jpg" />
          <robot-img src="//image-demo.oss-cn-hangzhou.aliyuncs.com/example.jpg" />
          <robot-img src="//image-demo.oss-cn-hangzhou.aliyuncs.com/example.jpg" />
          <robot-img src="//image-demo.oss-cn-hangzhou.aliyuncs.com/example.jpg" />
          <robot-img src="//image-demo.oss-cn-hangzhou.aliyuncs.com/example.jpg" />
          <robot-img src="//image-demo.oss-cn-hangzhou.aliyuncs.com/example.jpg" />
          <robot-img src="//image-demo.oss-cn-hangzhou.aliyuncs.com/example.jpg" />
          <robot-img src="//image-demo.oss-cn-hangzhou.aliyuncs.com/example.jpg" />
        </container>
      )
    },
  })
  createApp(App).mount(document.getElementById('50-container')!)
}

main()

自适应处理图片

图片组件会根据图片的面积自动适应,默认的判断方法:

function defaultShouldUpdate(newRect: DOMRect, oldRect: DOMRect) {
  // 当 width or height 变大 20% 时,才更新图片
  return newRect.width > oldRect.width * 1.2 || newRect.height > oldRect.height * 1.2
}

也可以通过给组件传 shouldUpdate 来定义图片是否需要更新,也可以通过

imgPool.reset({
  globalVars: { shouldUpdate: (newRect, oldRect) => true }
})

设置全家默认的 shouldUpdate

import './index.css'

import { createApp, defineComponent, reactive } from 'vue'

import {
  checkWebpSupported,
  createImgPool,
  createSrcTplOfAliOss,
  ImgProps,
  useImg,
} from '@robot-img/vue-img'

async function main() {
  // 阿里云,参考:https://help.aliyun.com/document_detail/44688.html
  const imgPoolAliOss = createImgPool({
    createSrcTpl: createSrcTplOfAliOss({
      webp: await checkWebpSupported(),
    }),
    globalVars: {
      className: 'resize-img-example',
    },
  })

  const ImgDiv = defineComponent({
    setup() {
      const props = reactive<ImgProps>({
        src: '//image-demo.oss-cn-hangzhou.aliyuncs.com/example.jpg',
        lazy: 'resize',
      })
      const { state, domRef, domProps } = useImg<HTMLDivElement>(props)

      const ratio = reactive({
        value: 1,
      })
      const handleAdd = () => {
        ratio.value = Math.min(3, ratio.value * 1.05)
      }
      const handleCut = () => {
        ratio.value = ratio.value * 0.95
      }

      return () => {
        let backgroundImage
        if (state.src) {
          backgroundImage = `url(${state.src})`
        }
        const style = {
          backgroundImage,
          width: `${100 * ratio.value}px`,
          height: `${80 * ratio.value}px`,
        }
        return (
          <>
            <p>
              <button onClick={handleAdd}>宽高变大10%</button>
              <button onClick={handleCut}>宽高变小10%</button>
            </p>
            <div {...domProps} ref={domRef} style={style} />
            <p>current src: ${state.src}</p>
          </>
        )
      }
    },
  })

  const App = defineComponent({
    components: {
      'robot-img': ImgDiv,
    },
    provide: {
      imgPool: imgPoolAliOss,
    },
    setup() {
      return () => (
        <div class="example-resize">
          <robot-img />
        </div>
      )
    },
  })
  createApp(App).mount(document.getElementById('60-resize')!)
}

main()