如何封装一个Vue3的Icon的组件——uni-app

前言

首先了解一下前端 icon 的发展史。

在前端刚发展起来时,大部分图标都是用 img 来实现的。渐渐发现一个页面的请求资源中图片 img 占了大部分,所以为了优化有了image sprite 就是所谓的雪碧图,就是将多个图片合成一个图片,然后利用 css 的 background-position 定位显示不同的 icon 图标。但这个也有一个很大的痛点,维护困难。每新增一个图标,都需要改动原始图片,还可能不小心出错影响到前面定位好的图片,而且一修改雪碧图,图片缓存就失效了,久而久之你不知道该怎么维护了。

font 库 后来渐渐地一个项目里几乎不会使用任何本地的图片了,而使用一些 font 库来实现页面图标。常见的如 Font Awesome iconfont

iconfont 图标及使用姿势可以参考iconfont使用

创建Icon组件

我们有了icon图标,接下来就是在的项目创建一个icon组件。

没有封装前,使用icon大概长这样

<text class="crab crab-gou" style="font-size: 28rpx; color: #333;">
复制代码

我们最后封装组件,大概长这样

<ca-icon name="crab-gou" />
复制代码

实现的大概的思路

  • 引入iconfont图标样式
  • 在传入大小名称和样式
<template>
  <text
    @tap.stop="handleClick"
    class="ca-icon"
    :class="[classPrefix_, name]"
    :style="[iconStyle_]"
  ></text>
</template>
<script>
import { defineComponent, toRefs, computed } from 'vue'

export default defineComponent({
  props: {
    name: {
      type: String,
      default: '',
    },
    size: {
      type: [String, Number],
      default: '28',
    },
    color: {
      type: [String, Boolean],
      default: 'inherit',
    },
    size: {
      type: [Number, String],
      default: 28,
    },
  },
  setup(props, { emit }) {
    const handleClick = (e) => {
      emit('click', e)
    }
    const iconStyle_ = computed(() => {
      // 组件样式
      const obj = {
        fontSize: String(props.size).indexOf('px') !== -1 ? props.size : uni.upx2px(props.size) + 'px',
      }
      // icon名称
    const classPrefix_ = computed(() => {
      const prevName = props.name.split('-')[0]
      const name = prevName === 'icon' ? 'iconfont' : prevName
      return name
    })
      String(props.color) !== 'false' && (obj['color'] = props.color)
      return obj
    })

    return {
      iconStyle_,
      classPrefix_,
      handleClick,
      ...toRefs(props),
    }
  },
})
</script>
<style lang="scss" scoped>
@import './font.css';
.ca-icon {
  font-size: 0;
  @include tst(transform);
  vertical-align: middle;
  position: relative;
  display: inline-block;
  font-size: inherit;
  // 浏览器渲染引擎如何渲染字体。浏览器会在速度、清晰度、几何精度之间进行权衡。
  text-rendering: auto;
  -webkit-font-smoothing: antialiased;
}
</style>
复制代码

嗯 一个简单的icon 组件封装好了。Vant中功能都有了,除了小点点,不想写,就是玩,觉得用处不大。

进一步改造

阿就这,就这样完了嘛?

同事开发项目中使用了觉得不满意,提了新的需求。

  • 我要图标可以旋转,一直转圈,转圈速度自己调节。
  • 我要图标可以旋转一定的角度。

行吧,安排

于是乎在原来的基础上添加几行代码,如下

<template>
  <text
    @tap.stop="handleClick"
    class="ca-icon"
    :class="[classPrefix_, name]"
    :style="[iconStyle_]"
  ></text>
</template>
<script >
import { defineComponent, toRefs, computed } from 'vue'

export default defineComponent({
  props: {
    name: {
      type: String,
      default: '',
    },
    size: {
      type: [String, Number],
      default: '28',
    },
    color: {
      type: [String, Boolean],
      default: 'inherit',
    },
    size: {
      type: [Number, String],
      default: 28,
    },
    spin: {
      type: Boolean,
      default: false,
    },
    spinGap: {
      type: [Number, String],
      default: 1.5,
    },
    rotate: {
      type: [Number, String, Boolean],
      default: false,
    },
  },
  setup(props, { emit }) {
    const handleClick = (e) => {
      emit('click', e)
    }
    const rotate_ = computed(() => {
      return String(props.rotate) === 'false' ? false : props.rotate
    })
    // class-prefix
    const classPrefix_ = computed(() => {
      const prevName = props.name.split('-')[0]
      const name = prevName === 'icon' ? 'iconfont' : prevName
      return name
    })

    const iconStyle_ = computed(() => {
      // 组件样式
      const obj = {
        fontSize:
          String(props.size).indexOf('px') !== -1
            ? props.size
            : uni.upx2px(props.size) + 'px',
      }
      String(props.color) !== 'false' && (obj['color'] = props.color)
      // 组件动画
      rotate_.value &&
        (obj.transform = isNaN(props.rotate)
          ? `rotate(${props.rotate})`
          : `rotate(${props.rotate}deg)`)
      if (props.spin) {
        obj.animation = `ca-spin ${props.spinGap}s linear infinite`
      }
      return obj
    })

    return {
      iconStyle_,
      classPrefix_,
      handleClick,
      ...toRefs(props),
    }
  },
})
</script>
<style lang="scss" scoped>
@import './font.css';

.ca-icon {
  font-size: 0;
  @include tst(transform);
  vertical-align: middle;
  position: relative;
  display: inline-block;
  font-size: inherit;
  // 浏览器渲染引擎如何渲染字体。浏览器会在速度、清晰度、几何精度之间进行权衡。
  text-rendering: auto;
  -webkit-font-smoothing: antialiased;
}
</style>
复制代码

scss 动画

// 旋转动画
@keyframes ca-spin {
    from {
        transform: rotate(0deg);
    }
    to {
        transform: rotate(360deg);
    }
}
复制代码

效果

    // 在代码中使用
    <view class="p-30"> icon </view>
    <ca-icon name="crab-gou" />
    <ca-icon name="crab-modal" color="#888" />
    <ca-icon name="crab-shijian1" size="60" />
    <ca-icon name="crab-aixin1" rotate="45" />
    <ca-icon name="crab-shoucang1" spin />
    <ca-icon name="crab-shoucang1" spin spinGap="5" />
复制代码

如图所示 Untitled

参考

猜你喜欢

转载自juejin.im/post/7034547946746019848