在Taro中开发一个跨端Svg组件,同时支持小程序、H5、React Native
Taro系列中一直没有跨端的绘图工具,小程序端支持canvas但是不支持svg,RN端有 react-native-svg 支持svg,但是没有很好原生的canvas插件,社区的canvas都是基于WebView实现的,或者skia,这个插件的书写方式和canvas有较大的差异
所以开发了这个兼容小程序、H5和React Native 的Svg组件,来实现跨端绘图
前言
在小程序上面,是不支持直接编写svg代码的,但是有间接的方式让他支持,就是使用Image渲染svg,或者使用css的背景图渲染一个svg,但是这样写有一定的局限性,例如要支持动画、组件事件,将svg保存到本地等都无法实现
所以我选择了使用Canvas来模拟开发svg的相关组件支持,在RN端则是使用现在已经有的 react-native-svg
这个插件,因此我们只需要在小程序和H5端模拟 react-native-svg
组件的功能,组件属性及其功能完全按照 react-native-svg
规范进行开发
当前组件属性支持
Svg中有大量的组件,用来绘制各种图形或者图片
已经支持的组件
- Svg
width
height
viewBox
preserveAspectRatio
- Rect
x
y
width
height
- Circle
cx
cy
r
- Ellipse
cx
cy
rx
ry
- Line
x1
y1
x2
y2
- Image
x
y
width
height
preserveAspectRatio
- Text
x
y
dx
dy
fontSize
fontWeight
fontFamily
textAnchor
fontStyle
- TSpan
x
y
dx
dy
fontSize
fontWeight
fontFamily
textAnchor
fontStyle
- Polyline
points
- Polygon
points
- Path
d
- Defs
- Use
href
x
y
- G
- LinearGradient
x1
y1
x2
y2
- RadialGradient
cx
cy
rx
ry
fx
fy
- Stop
offset
stopColor
stopOpacity
- ClipPath
支持的公共属性
- id
- style
- opacity
- fill
- fillOpacity
- stroke
- strokeWidth
- strokeOpacity
- strokeLinecap
- strokeLinejoin
- strokeDasharray
- strokeDashoffset
- origin
- originX
- originY
- translate
- translateX
- translateY
- rotation
- clipPath
- scale
- scaleX
- scaleY
- skew
- skewX
- skewY
- transform (如果要使用变换动画,需要使用这个属性实现,其他变换属性RN端不支持动画)
动画支持
因为选择了 react-native-svg
作为开发参考,因此在动画支持方面,也参照了RN的相关API Animated
和 Easing
下面是一个循环动画的使用示例
import {Svg, Rect,Animated, Easing
} from '@/duxui/components/Svg'
import { useEffect, useRef } from 'react'const AnimatedRect = Animated.createAnimatedComponent(Rect)const Loop = () => {const primary = duxappTheme.primaryColorconst secondary = duxappTheme.secondaryColorconst x = useRef(new Animated.Value(0)).currentconst size = 50useEffect(() => {setTimeout(() => {Animated.loop(Animated.timing(x, {toValue: 4,duration: 3000,easing: Easing.bounce,useNativeDriver: false})).start()}, 500)}, [x])const width = pxNum(702)const height = pxNum(360)return <GroupList.Item title='循环动画' desc='利用 interpolate 将动画值映射到任意范围'><Svg width={width} height={height}><AnimatedRectx={x.interpolate({inputRange: [0, 1, 2, 3, 4],outputRange: [0, width - size, width - size, 0, 0]})}y={x.interpolate({inputRange: [0, 1, 2, 3, 4],outputRange: [0, 0, height - size, height - size, 0]})}width={size}height={size}rx={10} ry={10} stroke={secondary} fill={primary}/></Svg></GroupList.Item>
}
组件事件
为何实现组件事件,模拟 React Native 的 PanResponder
API,在小程序和H5端实现了相关的功能
大多数组件都支持以下触摸事件
- onPress
- onPressIn
- onPressOut
- onLongPress
下面两个示例,一个展示点击事件,一个事件结合动画实现拖拽效果
事件示例
下面的代码我们能看到点击不同的组件将获得对应组件的事件,这三个形状是有覆盖关系的
const Event = () => {return <GroupList.Item title='组件事件' desc='点击对应的图形会有对应的点击事件'><Svg width={100} height={100}><Circlecx='50%'cy='50%'r='38%'fill='red'onPress={() => toast('点击圆形')}/><Rectx='20%'y='20%'width='60%'height='60%'fill='blue'onPress={() => toast('点击正方形')}/><Pathd='M50,5L20,99L95,39L5,39L80,99z'fill='pink'onPress={() => toast('点击五角星')}/></Svg></GroupList.Item>
}
结合动画
const TouchEvent = () => {const primary = duxappTheme.primaryColorconst secondary = duxappTheme.secondaryColorconst movePan = useRef(new Animated.ValueXY({ x: 10, y: 10 }, { useNativeDriver: false })).currentconst moveEvent = useRef(PanResponder.create({onMoveShouldSetPanResponder: () => true,onPanResponderGrant: () => {movePan.setOffset({x: movePan.x._value,y: movePan.y._value})},onPanResponderMove: (e, gestureState) => {movePan.setValue({ x: gestureState.dx, y: gestureState.dy })},onPanResponderRelease: () => {movePan.flattenOffset()}})).currentconst moveOriginPan = useRef(new Animated.ValueXY({ x: 150, y: 50 }, { useNativeDriver: true })).currentconst moveOriginEvent = useRef(PanResponder.create({onMoveShouldSetPanResponder: () => true,onPanResponderMove: (e, gestureState) => {moveOriginPan.setValue({ x: gestureState.dx + 150, y: gestureState.dy + 50 })},onPanResponderRelease: () => {Animated.timing(moveOriginPan, {toValue: { x: 150, y: 50 },duration: 600,easing: Easing.bounce,useNativeDriver: true}).start()}})).currentreturn <GroupList.Item title='结合动画' desc='蓝色拖拽,红色拖拽后回弹'><Svg width={pxNum(702)} height={pxNum(300)}><Rect width={pxNum(702)} height={pxNum(300)} fill='#fff' /><RectAnimatedwidth={50}height={50}fill={secondary}{...moveEvent.panHandlers}x={movePan.x}y={movePan.y}/><RectAnimatedwidth={50}height={50}fill={primary}{...moveOriginEvent.panHandlers}x={moveOriginPan.x}y={moveOriginPan.y}// translateX={moveOriginPan.x}// translateY={moveOriginPan.y}/></Svg></GroupList.Item>
}
转换为图片
为了方便导入为图片,还封装了一个 SvgToImage
组件,专门将Svg导出为图片方便下一步的处理,请前往文档查看具体的使用方法
继续
Svg相关功能是duxui库中提供的一个组件,详情请查看duxui文档
当前UI库中的二维码(QRCode)、签名(Sign) 组件都是使用这个Svg组件来实现的
如果你对这个项目有兴趣,可以查看文档,继续了解详情
Svg文档:http://duxapp.com/docs/duxui/svg
框架文档:http://duxapp.com
GitHub:https://github.com/duxapp
相关文章:

在Taro中开发一个跨端Svg组件,同时支持小程序、H5、React Native
Taro系列中一直没有跨端的绘图工具,小程序端支持canvas但是不支持svg,RN端有 react-native-svg 支持svg,但是没有很好原生的canvas插件,社区的canvas都是基于WebView实现的,或者skia,这个插件的书写方式和c…...

【办公类-100-01】20250515手机导出教学照片,自动上传csdn+最大化、最小化Vs界面
背景说明: 每次把教学照片上传csdn,都需要打开相册,一张张截图,然后ctrlV黏贴到CSDN内,我觉得太烦了。 改进思路: 是否可以先把所有照片都上传到csdn,然后再一张张的截图(去掉幼儿…...
SIP协议栈--osip源码梳理
文章目录 osiposip主体结构体code main函数 状态机转化结构体code状态转换 sip事务结构体code osip_dialog结构体code 创建并发送200 OK响应 osip_message结构体code osip_eventcode 打印接收到的SIP消息 osip OSIP(Open Source Implementation of SIP)…...

Python零基础入门到高手8.4节: 元组与列表的区别
目录 8.4.1 不可变数据类型 8.4.2 可变数据类型 8.4.3 元组与列表的区别 8.4.4 今天彩票没中奖 8.4.1 不可变数据类型 不可变数据类型是指不可以对该数据类型进行原地修改,即只读的数据类型。迄今为止学过的不可变数据类型有字符串,元组。 在使用[]…...

深度学习入门:深度学习(完结)
目录 1、加深网络1.1 向更深的网络出发1.2 进一步提高识别精度1.3 加深层的动机 2、深度学习的小历史2.1 ImageNet2.2 VGG2.3 GoogleNet2.4 ResNet 3、深度学习的高速化3.1 需要努力解决的问题3.2 基于GPU的高速化3.3 分布式学习3.4 运算精度的位数缩减 4、深度学习的应用案例4…...
OpenCV CUDA模块中矩阵操作------矩阵元素求和
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在OpenCV的CUDA模块中,矩阵元素求和类函数主要用于计算矩阵元素的总和、绝对值之和以及平方和。这些操作对于图像处理中的特征提取、…...

使用Scrapeless Scraping Browser的自动化和网页抓取最佳实践
引言:人工智能时代浏览器自动化和数据收集的新范式 随着生成性人工智能、人工智能代理和数据密集型应用程序的快速崛起,浏览器正在从传统的“用户互动工具”演变为智能系统的“数据执行引擎”。在这一新范式中,许多任务不再依赖单一的API端点…...

java数组题(5)
(1): 思路: 1.首先要对数组nums排序,这样两数之间的差距最小。 2.题目要求我们通过最多 k 次递增操作,使数组中某个元素的频数(出现次数)最大化。经过上面的排序,最大数…...
使用Thrust库实现异步操作与回调函数
文章目录 使用Thrust库实现异步操作与回调函数基本异步操作插入回调函数更复杂的回调示例注意事项 使用Thrust库实现异步操作与回调函数 在Thrust库中,你可以通过CUDA流(stream)来实现异步操作,并在适当的位置插入回调函数。以下是如何实现的详细说明&a…...

物联网无线传感方向专业词汇解释
涡旋电磁波(VEMW):一种具有轨道角动量的电磁波,其特性在于能够在传播过程中携带额外的相位信息,从而增加通信系统的容量和灵活性。波前:波动传播过程中,同一时刻振动相位相同的所有点构成的几何曲面,代表波…...

Maven 插件参数注入与Mojo开发详解
🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编…...
C++中void*知识详解和注意事项
一、void* 是什么? 在 C/C 中,void* 表示一个通用指针类型(generic pointer),可以指向任意类型的对象,但 不能直接解引用或进行算术运算,必须先进行类型转换。 void* ptr; // 可以指向任意类型…...

2024年全国青少年信息素养大赛——算法创意实践挑战赛复赛真题(小学组)——玫瑰花地的面积
2024年全国青少年信息素养大赛——算法创意实践挑战赛复赛真题(小学组)——玫瑰花地的面积 上面试卷可点下方,支持在线编程,在线测评~ 2024年全国信息素养大赛 算法创意实践挑战赛复赛(小学组)_c_少儿编程题库学习中心-嗨信奥 5月17号 全国青…...

【补充笔记】修复“NameError: name ‘ZhNormalizer‘ is not defined”的直接方法
#工作记录 一、问题描述 在运行CosyVoice_For_Windows项目时,出现以下报错: File "F:\PythonProjects\CosyVoice_For_Windows\cosyvoice\cli\frontend.py", line 74, in __init__ self.zh_tn_model ZhNormalizer(remove_erhuaFalse, fu…...

预训练模型实战手册:用BERT/GPT-2微调实现10倍效率提升,Hugging Face生态下的迁移学习全链路实践
更多AI大模型应用开发学习内容,尽在聚客AI学院。 一. 预训练模型(PTM)核心概念 1.1 什么是预训练模型? 预训练模型(Pre-trained Model, PTM)是在大规模通用数据上预先训练的模型,通过自监督学…...
并发笔记-给数据上锁(二)
文章目录 核心挑战 (The CRUX)29.1 并发计数器 (Concurrent Counters)1. 简单非并发计数器 (Figure 29.1)2. 同步计数器(单锁版本 - Coarse-Grained Lock, Figure 29.2)3. 可伸缩计数:近似/懒惰计数器 (Approximate/Sloppy Counter, Figure 2…...

mac docker弹窗提示Docker 启动没有响应
一、原因分析 这台笔记电脑是Mac M3操作系统,安装Docker之后,Docker应用程序一直启动不起来。 二、解决办法 sudo rm /Library/PrivilegedHelperTools/com.docker.vmnetd sudo cp /Applications/Docker.app/Contents/Library/LaunchServices/com.docker.vmnetd /Library/Pri…...
每日算法刷题计划Day7 5.15:leetcode滑动窗口4道题,用时1h
一.定长滑动窗口 【套路】教你解决定长滑窗!适用于所有定长滑窗题目! 模版套路 1.题目描述 1.计算所有长度恰好为 k 的子串中,最多可以包含多少个元音字母 2.找出平均数最大且 长度为 k 的连续子数组,并输出该最大平均数。 3.…...
如何利用 Python 爬虫按关键字搜索京东商品:实战指南
在电商领域,京东作为国内知名的电商平台,拥有海量的商品数据。通过 Python 爬虫技术,我们可以高效地按关键字搜索京东商品,并获取其详细信息。这些信息对于市场分析、选品上架、库存管理和价格策略制定等方面具有重要价值。本文将…...

Ubuntu 22.04搭建OpenStreeMap地址解析服务(保姆级教程)
1.数据准备 1.1.全球数据 下载地址:https://planet.openstreetmap.org/ 1.2.特定区域的数据 下载地址:Geofabrik Download Server 2.安装必要的软件包 2.1.更新系统软件包 sudo apt updatesudo apt upgrade 2.2.安装所需要的软件包 执行下面的命…...

sqli—labs第五关——报错注入
一:判断输入类型 首先测试 ?id1 回显You are in... 渐进测试?id1 报错分析: 出现引号提示——“”,可能是字符型 继续测试?id1--(用注释符修复了语法错误) 回显You are in... 说明就是字符型 因为能用注释符…...

从海洋生物找灵感:造个机器人RoboPteropod,它能在水下干啥?
大家好!在如今人类对水下环境探索不断深入的时代,从水下考古到珊瑚礁考察,各种任务都离不开水下机器人的助力。但传统水下机器人尺寸较大,在狭窄的水下空间施展不开。今天,我们就来认识一款受海洋小生物启发而设计的仿…...

FastAPI系列16:从API文档到TypeScript 前端客户端(SDKs)
从API文档到TypeScript 前端客户端(SDKs) 快速入门生成一个TypeScript 客户端测试生成的TypeScript 客户端 API标签与客户端生成生成带有标签的 TypeScript 客户端 自定义Operation ID使用自定义Operation ID生成TypeScript客户端 在 FastAPI系列15&…...
为什么 Redis 设计为单线程?6.0 版本为何引入多线程?
Redis 6.0引入多线程的核心目的是优化网络I/O处理,通过分离I/O操作与命令执行,在保持数据一致性的前提下,充分利用多核CPU资源提升高并发场景下的性能,同时保持向后兼容性。以下是对Redis单线程设计与6.0版本引入多线程的详细解析…...
C# 使用HttpClient下载文件
本章讲述:如何在C#中使用HttpClient直接从阿里云OSS下载文件。 步骤1: 添加必要的命名空间 using System; using System.IO; using System.Net.Http; 步骤2: 创建下载方法 以下是使用HttpClient下载文件的示例代码: public class OssDownloader {//d…...

CS016-2-unity ecs
目录 【23】射击改进 【24】僵尸生成器 编辑【25】随机行走 【27】射击光效 【23】射击改进 a. 当距离目标太远的时候,要继续移动。而当距离目标到达攻击距离之后,则停止移动。 上图中的if:判断自身和目标的距离是否大于攻击距离&#…...

CST软件对OPERACST软件联合仿真汽车无线充电站对人体的影响
上海又收紧了新能源车的免费上牌政策。所以年前一些伙伴和我探讨过买新能源汽车的问题,小伙伴们基本纠结的点是买插电还是纯电?我个人是很抗拒新能源车的,也开过坐过。个人有几个观点: 溢价过高,不保值。实际并不环保…...

华为2024年报:鸿蒙生态正在取得历史性突破
华为于2025年03月31日发布2024年年度报告。报告显示,华为经营结果符合预期,实现全球销售收入 8,621 亿元人民币,净利润 626 亿元人民币。2024 年研发投入达到 1,797 亿元人民币,约占全年收入的 20.8%,近十年累计投入的…...
策略模式-枚举实现
策略模式的实现方法有很多,可以通过策略类if,else实现。下面是用枚举类实现策略模式的方法。 定义一个枚举类,枚举类有抽象方法,每个枚举都实现抽象方法。这个策略,实现方法是工具类的很实现,代码简单好理解 枚举实现…...
C++中多重继承下的虚表结构
在 C 的多重继承 中,虚表(vtable)结构会变得更加复杂。 一、基础回顾:单继承下的虚表结构 类中含有虚函数 → 编译器生成虚表(每类一张);每个对象有一个隐藏的虚表指针(vptr&#x…...