阅读 295

Taro封装兼容H5及微信小程序的圆形进度条组件

一、背景

由于各种原因,九月异动换了一个部门。技术栈也从vue、微前端换成了react,taro;从做tob系统换成了小程序、h5多端。算算,现在在新部门已经待了两个月,整体感觉换部门还算是正确的选择,接触到一些新技术及场景,部门整体氛围也不错...更多就不再这里赘述了,打算年终总结的时候好好写一写。

好了,以下才是本文真正的背景:

笔者现在主要做c端业务,技术栈taro+ts,流量主要在小程序+H5。在业务高速发展中,我们意识到,沉淀一个组件库的必要性。熟悉Taro的朋友,应该知道Taro官方提供了Taro-ui,用过的朋友肯定也知道,好用的组件是在是没几个...很多都得自己封装,业内除了taro-ui也无其他适配多端的组件库可用,所以我们决定自己搭建一个组件库。感兴趣的朋友可以关注本专栏,后续会持续更新文章。

本文讲解如何用taro封装一个圆形进度条组件,重难点在技术实现选型以及如何用svg适配H5及微信小程序

二、效果图

image.png

三、参数设计

参数说明类型默认值
percent百分比number0
size圆环直径,单位为pxnumber100
color进度条颜色string#DC8D20
layerColor轨道背景颜色string#EFEFEF
text文字string-
strokeWidth进度条宽度,单位为pxnumber4

四、技术实现

常见实现方案:

  • canvas

  • svg

(1)canvas

我首先尝试用canvas实现,taro提供了canvas API,画出来之后,发现小程序是没有问题的,但是H5控制台直接报错 image.png 仔细翻看文档后,发现canvas API果然不支持H5 image.png

(2)svg

于是我想到了svg,简单的熟悉了下svg之后,我尝试拿svg画图,代码实现如下

circle标签画圆,首先解释下标签的基本属性

  • svg

    • 前两个参数是对svg元素做位移使用,通常设置为0;

    • 后两个参数表示svg元素可容纳的大小,通常设置为宽高

    • height:svg元素的高

    • width:svg元素的宽

    • viewBox:

  • circle

    • cx:圆心的x坐标位置

    • cy:圆心的y坐标位置

    • r: 表示半径

    • fill: 圆的填充色

    • stroke-width: 边框宽度

    • stroke:边框填充色

    • stroke-dasharray:控制画笔的虚实,通过实线和虚线的来控制画

    • stroke-dashoffset:相对于绘制的起点偏移的量

    • stroke-linecap:在开放子路径被设置描边的情况下,用于开放自路径两端的形状

可以看到有两个circle标签,一个是背景色,一个是进度条色,如下图

image.png

  • heightwidth均用props-size(圆环直径);那么圆心的x,y坐标就均是size / 2,这很好理解

  • 半径r这里需要注意,等于(size - strokeWidth * 2) / 2,也就是(直径-边框宽度*2) / 2,而非直径 / 2

  • 圆的填充色fill均是none,因为是空心圆

  • strokeWidth边框宽度直接用props-strokeWidth即可

  • stroke,边框填充色,背景圆用props-layerColor,上面的圆用props-color

以上属性画好了两个圆,一个是背景色的圆,一个是进度条圆,背景圆整个圆周长都填充,进度条圆,需要根据百分比填充,如何实现呢,利用strokeDasharraystrokeDashoffset进行偏移

  • strokeDasharray设为圆周长2*π*r,也就是2 * Math.PI * r

  • strokeDashoffset偏移圆周长 * (1 - percent / 100),比如percent=25,偏移圆周长*0.75的长度

props具体含义在二、参数设计中已说明

import { View } from '@tarojs/components' render() { const { percent, size, strokeWidth, layerColor, color, text } = this.props const r = (size - strokeWidth * 2) / 2 const circumference = 2 * Math.PI * r return (     <View>         <svg height={size} width={size} viewBox={`0 0 ${size} ${size}`}>             <circle id="circleBg"               cx={size / 2}               cy={size / 2}               r={r}               fill="none"               strokeWidth={strokeWidth}               stroke={layerColor}             />             <circle id="circle"               style={{ transform: 'rotate(-90deg)', transformOrigin: ' 50% 50%' }}               cx={size / 2}               cy={size / 2}               r={r}               fill="none"               strokeWidth={strokeWidth}               stroke={color}               strokeDasharray={circumference}               strokeDashoffset={circumference * (1 - percent / 100)}               strokeLinecap='round'             />         </svg>     </View>   ) } 复制代码

画完之后,尴尬的事情来了,H5是没问题的,而微信小程序出不来,微信小程序控制台报错如下 image.png

于是,我又仔细的去翻看文档,查到微信小程序目前不支持 SVG 标签,仅仅支持加载 SVG 之后 作为 background-image 进行展示,如 background-image: url("data:image/svg+xml.......),或者 base64 后作为 background-image 的 url,可见社区官方文章

image.png

于是尝试用background-image: url("")的方式尝试兼容小程序

静态的svg可以直接在自动转换的网站上转换成base64,由于这里的svg是有变量的,所以我们手动转换,查到<替换成%3C>替换成%3E;但是怎么也出不来,最后在这个网站与自动替换静态svg的对比,发现是颜色值的#也需要相应的替换成%23,替换了就可以了!!!代码如下

private transColor = (color) => {     return color.replace('#', '%23') } render() {      const style: React.CSSProperties = {       backgroundImage: `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='${size}' width='${size}' viewBox='0 0 ${size} ${size}'%3E%3Ccircle id='circleBg' cx='${size / 2}' cy='${size / 2}' r='${r}' fill='none' stroke-width='${strokeWidth}' stroke='${this.transColor(layerColor)}'%3E%3C/circle%3E%3Ccircle id='circle' cx='${size / 2}' cy='${size / 2}' r='${r}' fill='none' stroke-width='${strokeWidth}' stroke='${this.transColor(color)}' stroke-dasharray='${circumference}' stroke-dashoffset='${circumference * (1 - percent / 100)}' stroke-linecap='round' style='transform: rotate(-90deg); transform-origin: 50%25 50%25;'%3E%3C/circle%3E%3C/svg%3E")`,       width: size + 'px',       height: size + 'px'     }     return(         <view             style={style}         >         </view>     ) } 复制代码

到此也算是封装完成了,用svg的方式兼容了H5及小程序

image.png

五、组件基本使用及完整代码

(1)基本使用

基础用法

<ProgressCircle percent={25} text="25%" /> 复制代码

自定义颜色

<ProgressCircle percent={25} color="#FF2929" text="自定义颜色" /> 复制代码

自定义圆环直径

<ProgressCircle percent={25} size={200} text="自定义圆环直径" /> 复制代码

自定义进度条宽度

<ProgressCircle percent={25} size={200} strokeWidth={10} text="自定义进度条宽度" /> 复制代码

(2)完整代码

//  import React from 'react' import { View, Text } from '@tarojs/components' import { ProgressCircleProps } from '../../../types/progress-circle' import Taro from '@tarojs/taro' export default class ProgressCircle extends React.Component<ProgressCircleProps> {   public static defaultProps: ProgressCircleProps   private transColor = (color) => {     return color.replace('#', '%23')   }   public render(): JSX.Element | null {     const { percent, size, strokeWidth, layerColor, color, text } = this.props     const r = (size - strokeWidth * 2) / 2     const circumference = 2 * Math.PI * r     const isWeapp = Taro.getEnv() === Taro.ENV_TYPE.WEAPP     const style: React.CSSProperties = {       // backgroundImage这一句在掘金这个编写器会导致下面的代码高亮异常,所以注释掉,其实是有用的哈       // backgroundImage: `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='${size}' width='${size}' viewBox='0 0 ${size} ${size}'%3E%3Ccircle id='circleBg' cx='${size / 2}' cy='${size / 2}' r='${r}' fill='none' stroke-width='${strokeWidth}' stroke='${this.transColor(layerColor)}'%3E%3C/circle%3E%3Ccircle id='circle' cx='${size / 2}' cy='${size / 2}' r='${r}' fill='none' stroke-width='${strokeWidth}' stroke='${this.transColor(color)}' stroke-dasharray='${circumference}' stroke-dashoffset='${circumference * (1 - percent / 100)}' stroke-linecap='round' style='transform: rotate(-90deg); transform-origin: 50%25 50%25;'%3E%3C/circle%3E%3C/svg%3E")`,       width: size + 'px',       height: size + 'px'     }     return (       <View className="circle" style={`width:${size}px; height:${size}px`}>                  {!isWeapp &&           <svg height={size} width={size} viewBox={`0 0 ${size} ${size}`}>             <circle id="circleBg"               cx={size / 2}               cy={size / 2}               r={r}               fill="none"               strokeWidth={strokeWidth}               stroke={layerColor}             />             <circle id="circle"               style={{ transform: 'rotate(-90deg)', transformOrigin: ' 50% 50%' }}               cx={size / 2}               cy={size / 2}               r={r}               fill="none"               strokeWidth={strokeWidth}               stroke={color}               strokeDasharray={circumference}               strokeDashoffset={circumference * (1 - percent / 100)}               strokeLinecap='round'             />           </svg>}         {isWeapp &&           <View>             <view               style={style}             >             </view>           </View>}         <Text className="circle-text">{text}</Text>       </View>     )   } } ProgressCircle.defaultProps = {   percent: 0,   size: 100,   strokeWidth: 4,   layerColor: '#EFEFEF',   color: '#DC8D20' } 复制代码

export interface ProgressCircleProps {     /**     * 百分比     * @default 0     */     percent: number     /**      * 圆环直径,单位为px      * @default 100      */     size: number     /**      * 进度条颜色      *  @default #DC8D20      */     color: string     /**      * 轨道背景颜色      *  @default #EFEFEF      */     layerColor: string     /**      * 文字      */     text?: string     /**      * 进度条宽度,单位为px      * @default 4      */     strokeWidth: number } 复制代码

.circle {   position: relative;   &-text {     position: absolute;     width: 100%;     top: 50%;     left: 50%;     text-align: center;     transform: translate(-50%, -50%);   } }


作者:jjjona0215
链接:https://juejin.cn/post/7027739818666754079


文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐