鸿蒙5:HarmonyOS应用开发-部分原生能力
·
注意:博主有个鸿蒙专栏,里面从上到下有关于鸿蒙next的教学文档,大家感兴趣可以学习下
如果大家觉得博主文章写的好的话,可以点下关注,博主会一直更新鸿蒙next相关知识
目录
4. 鸿蒙原生能力
4.1 音视频播放

arkUI提供了Video组件可以直接播放视频,并提供自带的播放-暂停 全屏,拖动进度等功能
用法
- Video提供构造参数 Video({ src: string | Resource })
- src支持在线路径-和本地资源路径
- 示例
Video({
src:'https://video19.ifeng.com/video09/2024/05/23/p7199260608686989961-0-122707.mp4'
})
版权说明: 上述代码中的视频链接为参考学习,并非用作商业用途,请同学们自行放置的外链视频链接

- 放映本地视频
本地视频我们需要放置在资源目录的原始文件中rawfile目录下,使用$rawfile函数来获取路径进行赋值即可
Video({
src: $rawfile('hm.mp4')
})
.width('100%')
.aspectRatio(1.4)
- 完整代码
@Entry
@Component
struct VideoCase {
build() {
Row() {
Column() {
Tabs(){
TabContent(){
Video({
src:'https://video19.ifeng.com/video09/2024/05/23/p7199260608686989961-0-122707.mp4'
})
}.tabBar('在线视频')
TabContent(){
Video({
src:$rawfile('hm.mp4')
})
}.tabBar('本地视频')
}
}
.width('100%')
}
.height('100%')
}
}
- 视频控制-播放-暂停--倍速-全屏-进度
我们可以通过构造函数传入currentProgressRate 控制倍速,它来自PlaybackSpeed的枚举,目前支持
0.75-1-1.25-1.75-2倍速设置
- 同时我们可以通过传入VideoController来获取视频播放的控制权
- 实现控制倍速播放
@Entry
@Component
struct VideoCase {
@State
speed: number = 1
build() {
Row() {
Tabs() {
TabContent() {
Column({ space: 20 }) {
Video({
currentProgressRate: this.speed,
src: 'https://vd3.bdstatic.com/mda-pmj5ajqd7p4b6pgb/576p/h264/1703044058699262355/mda-pmj5ajqd7p4b6pgb.mp4?auth_key=1703138418-0-0-618ea72b33be241c96c6cff86c06e080&bcevod_channel=searchbox_feed&cr=1&cd=0&pd=1&pt=4&logid=0018430194&vid=9762003448174112444&abtest=all'
})
.width('100%')
.aspectRatio(1.4)
Slider({
value: this.speed,
min: 0.75,
step: 0.25,
max: 2,
style: SliderStyle.InSet
})
.showSteps(true)
.onChange(value => {
this.speed = value
})
Text(this.speed+"倍速").fontSize(14).textAlign(TextAlign.Center).width('100%')
}
.width('100%')
}.tabBar("在线视频")
TabContent() {
Video({
src: $rawfile('hm.mp4')
})
.width('100%')
.aspectRatio(1.4)
}
.tabBar("本地视频")
}
.animationDuration(300)
}
.height('100%')
}
}
- 实现通过controller控制视频 暂停- 播放-停止-全屏-静音-播放速度- 播放进度

自定义controller,手动控制视频播放
import { media } from '@kit.MediaKit'
import { image } from '@kit.ImageKit'
@Entry
@Component
struct VideoControlCase {
@State
currentSpeed: number = 1
@State
isMuted: boolean = false
@State
showController: boolean = false
@State
currentTime: number = 0
@State
videoTime: number = 0
controller: VideoController = new VideoController()
@State previewImage: image.PixelMap | undefined = undefined
build() {
Row() {
Column({ space: 20 }) {
Stack({ alignContent: Alignment.BottomEnd }) {
Video({
// 视频源
src: $rawfile('hm.mp4'),
// 封面图
previewUri: this.previewImage,
// 倍速 0.75 ~ 2,0.25一个档
currentProgressRate: this.currentSpeed,
// 控制器
controller: this.controller
})
.height(400)
.objectFit(ImageFit.Contain)// 填充模式
.autoPlay(true)// 自动播放
.loop(true)// 循环播放
.muted(this.isMuted)// 是否静音
.controls(this.showController)//是否展示控制栏
.onAppear(async () => {
//获取视频的内部的截图
const generator = await media.createAVImageGenerator()
generator.fdSrc = await getContext().resourceManager.getRawFd('1.mp4')
const previewUri =
await generator.fetchFrameByTime(5000, media.AVImageQueryOptions.AV_IMAGE_QUERY_NEXT_SYNC,
{ height: 400, width: 300 })
this.previewImage = previewUri
})
.onPrepared((time) => { // 视频准备好了可以获取视频的时长
this.videoTime = time.duration
})
.onUpdate((time) => { // 视频播放中可以获取播放的时长
this.currentTime = time.time
})
.onFullscreenChange((screen) => { // 根据是否全屏判断是否展示控制条
this.showController = screen.fullscreen
})
Row() {
Button('全屏')
.onClick(() => {
this.controller.requestFullscreen(true)
})
// 一般不需要手动全屏,可以过几秒自动退出,提示该充值了!
// Button('退出全屏')
// .onClick(() => {
// this.controller.exitFullscreen()
// })
}
}
Row({ space: 20 }) {
Text('播放进度:')
Slider({
value: $$this.currentTime,
min: 0,
max: this.videoTime,
})
.layoutWeight(1)// 改变时设置视频播放时长
.onChange((val) => {
this.controller.setCurrentTime(val)
})
}
.padding(20)
Row({ space: 20 }) {
Text('播放速度:')
Slider({
value: $$this.currentSpeed,
min: 0.75,
max: 2,
step: 0.25
})
.layoutWeight(1)
}
.padding(20)
Row({ space: 20 }) {
Button('播放')
.onClick(() => {
this.controller.start()
})
Button('暂停')
.onClick(() => {
this.controller.pause()
})
Button('停止')
.onClick(() => {
this.controller.stop()
})
Button('静音')
.onClick(() => {
this.isMuted = !this.isMuted
})
}
}
.width('100%')
}
.height('100%')
}
}
同理- 如果我们想播放一段音频-用同样的方式给到我们的Video的src属性就可以了Video同时支持
4.2 抖音小案例

- 声明类型和数据
class VideoItem {
videoUrl: string = ''
title: string = ""
}
const allData: VideoItem[] = [
{
videoUrl: 'https://vd4.bdstatic.com/mda-pmia5y0htmibjej2/576p/h264/1702970058650094297/mda-pmia5y0htmibjej2.mp4?auth_key=1703155514-0-0-a92de0b6c32239b242d0e51b151ee2d6&bcevod_channel=searchbox_feed&cr=1&cd=0&pd=1&pt=4&logid=2714832517&vid=9811936085320099438&abtest=all',
title: '我们只是拿某站的数据进行一下测试'
},
{
title: '请大家自行准备在线素材',
videoUrl: 'https://vd4.bdstatic.com/mda-pmjxx4ccc8x719t3/hd/h264/1703111503445924222/mda-pmjxx4ccc8x719t3.mp4?auth_key=1703155561-0-0-e7c32efbedae026e0e17c900bbd0cf55&bcevod_channel=searchbox_feed&pd=1&cr=1&cd=0&pt=4&logid=2761194416&vid=7476642150019887968&abtest=all'
},
{
title: '你知道冬天的雪是什么颜色吗, 我猜你不知道',
videoUrl: 'https://vd4.bdstatic.com/mda-pku9q3zt0rzybip0/hd/cae_h264/1701381974031001593/mda-pku9q3zt0rzybip0.mp4?v_from_s=hkapp-haokan-hbf&auth_key=1703155589-0-0-133df5be4b625ce34e1a75fe3a4baabf&bcevod_channel=searchbox_feed&pd=1&cr=1&cd=0&pt=4&logid=2789259407&vid=4775310688475056528&abtest=all'
},
{
title: '宝子们,我当众社死了,我竟然在众目睽睽之下完成了自己人生中的第一段程序',
videoUrl: 'https://vd2.bdstatic.com/mda-pkkf9qb7zksdaqs9/576p/h264/1700564765354260319/mda-pkkf9qb7zksdaqs9.mp4?v_from_s=hkapp-haokan-hbf&auth_key=1703155630-0-0-9a47a2910e8d5d90b47ba709fa530b5e&bcevod_channel=searchbox_feed&pd=1&cr=1&cd=0&pt=4&logid=2830328412&vid=8335346471874826776&abtest=all'
},
{
title: '文学,可以在寂静的夜用曼妙的文字勾勒出关于人生,职场,感情的诸多情绪,无奈此生当为程序员',
videoUrl: 'https://vd2.bdstatic.com/mda-pj8qa65bc9r1v1cf/576p/h264/1696871444324088416/mda-pj8qa65bc9r1v1cf.mp4?auth_key=1703155654-0-0-fdc0ca9c37ec26be3da9809b89e6151c&bcevod_channel=searchbox_feed&pd=1&cr=1&cd=0&pt=4&logid=2854467125&vid=5483608480722064830&abtest=all'
},
{
title: '当你阅读到这段文字的时候,我早已入睡,当我在睡梦中惊醒,你却早已安然睡去',
videoUrl: 'https://vd2.bdstatic.com/mda-pmexhyfui3e6rbmd/hd/cae_h264/1702705379314308540/mda-pmexhyfui3e6rbmd.mp4?auth_key=1703155684-0-0-5b0145fb4c2ec2f0d1bbd525ddb3d592&bcevod_channel=searchbox_feed&pd=1&cr=1&cd=0&pt=4&logid=2884962294&vid=3059586091403538183&abtest=all'
},
{
title: '每个人的内心都有一段独处的幽闭,不可诉说的窒息感孤独感在每当我们沉静下来的时候变愈发强烈',
videoUrl: 'https://vd3.bdstatic.com/mda-pmbgjjpkihkf7tjd/576p/h264/1702381478247675613/mda-pmbgjjpkihkf7tjd.mp4?v_from_s=hkapp-haokan-hbf&auth_key=1703155722-0-0-ea3c2453fbbb2cca66b12e9afe3d419f&bcevod_channel=searchbox_feed&pd=1&cr=1&cd=0&pt=4&logid=2922207105&vid=9050628586030215591&abtest=all'
},
{
title: '如果在未来的某一天,某一个早晨 晚上 瞬间,你会偶然想起多年前的一段往事,其实并不是我们有多怀旧,只是因为我们走过了太多的路',
videoUrl: 'https://vd2.bdstatic.com/mda-pj7ktq9euqchetdc/cae_h264/1696824500894354779/mda-pj7ktq9euqchetdc.mp4?v_from_s=hkapp-haokan-hbf&auth_key=1703155751-0-0-fccb0f110a3b447af67eb0feeabf06ad&bcevod_channel=searchbox_feed&pd=1&cr=0&cd=0&pt=4&logid=2951492012&vid=12162674818438199896'
},
{
title: '什么是知己,有个网红说,当你理解所有人的时候,你一定不能被所有人理解,每个人都或多或少的自私,只是或多或少而已',
videoUrl: 'https://vd3.bdstatic.com/mda-pmh5hr95fg6u8u0k/hd/cae_h264/1702877143957184120/mda-pmh5hr95fg6u8u0k.mp4?v_from_s=hkapp-haokan-hbf&auth_key=1703155785-0-0-5cfc2be95d00306082c7875a747dd998&bcevod_channel=searchbox_feed&pd=1&cr=1&cd=0&pt=4&logid=2985314718&vid=2720370579167170031&abtest=all'
}
]
- 实现代码
@Entry
@Component
struct DouyinCase {
@State
list: VideoItem[] = allData
@State
activeIndex: number = 0
build() {
Swiper() {
// 循环的数据 抖音的列表数据
ForEach(this.list, (item: VideoItem, index: number) => {
// 封装单独的组件实现 Video组件
VideoComp({
item,
index,
activeIndex: this.activeIndex
})
})
}
.index($$this.activeIndex)
.cachedCount(3)
.vertical(true)
.indicator(false)
.width('100%')
.height('100%')
}
}
@Component
struct VideoComp {
item: VideoItem = new VideoItem()
index: number = -1
@Require
@Prop
@Watch('changeVideo')
activeIndex: number
changeVideo() {
this.activeIndex === this.index ? this.controller.start() : this.controller.pause()
}
controller: VideoController = new VideoController()
@State
isPlay:boolean = true
build() {
Stack({ alignContent: Alignment.Bottom }) {
Stack(){
Video({
src: this.item.videoUrl,
controller: this.controller
})
.controls(false)
.objectFit(ImageFit.Contain)
.autoPlay(this.activeIndex === this.index ? true : false)
.loop(true)
.onPause(()=>{
this.isPlay = false
})
.onStart(()=>{
this.isPlay = true
})
.onClick(()=>{
this.isPlay?this.controller.pause():this.controller.start()
})
if(!this.isPlay){
Image($r('sys.media.ohos_ic_public_play'))
.width(100)
.aspectRatio(1)
.fillColor('#ccc')
.onClick(()=>{
this.controller.start()
})
}
}
Text(this.item.title)
.fontSize(14)
.fontColor(Color.White)
.padding(20)
}
}
}
class VideoItem {
videoUrl: string = ''
title: string = ""
}
const allData: VideoItem[] = [
{
videoUrl: 'https://vd4.bdstatic.com/mda-pmia5y0htmibjej2/576p/h264/1702970058650094297/mda-pmia5y0htmibjej2.mp4?auth_key=1703155514-0-0-a92de0b6c32239b242d0e51b151ee2d6&bcevod_channel=searchbox_feed&cr=1&cd=0&pd=1&pt=4&logid=2714832517&vid=9811936085320099438&abtest=all',
title: '我们只是拿某站的数据进行一下测试'
},
{
title: '请大家自行准备在线素材',
videoUrl: 'https://vd4.bdstatic.com/mda-pmjxx4ccc8x719t3/hd/h264/1703111503445924222/mda-pmjxx4ccc8x719t3.mp4?auth_key=1703155561-0-0-e7c32efbedae026e0e17c900bbd0cf55&bcevod_channel=searchbox_feed&pd=1&cr=1&cd=0&pt=4&logid=2761194416&vid=7476642150019887968&abtest=all'
},
{
title: '你知道冬天的雪是什么颜色吗, 我猜你不知道',
videoUrl: 'https://vd4.bdstatic.com/mda-pku9q3zt0rzybip0/hd/cae_h264/1701381974031001593/mda-pku9q3zt0rzybip0.mp4?v_from_s=hkapp-haokan-hbf&auth_key=1703155589-0-0-133df5be4b625ce34e1a75fe3a4baabf&bcevod_channel=searchbox_feed&pd=1&cr=1&cd=0&pt=4&logid=2789259407&vid=4775310688475056528&abtest=all'
},
{
title: '宝子们,我当众社死了,我竟然在众目睽睽之下完成了自己人生中的第一段程序',
videoUrl: 'https://vd2.bdstatic.com/mda-pkkf9qb7zksdaqs9/576p/h264/1700564765354260319/mda-pkkf9qb7zksdaqs9.mp4?v_from_s=hkapp-haokan-hbf&auth_key=1703155630-0-0-9a47a2910e8d5d90b47ba709fa530b5e&bcevod_channel=searchbox_feed&pd=1&cr=1&cd=0&pt=4&logid=2830328412&vid=8335346471874826776&abtest=all'
},
{
title: '文学,可以在寂静的夜用曼妙的文字勾勒出关于人生,职场,感情的诸多情绪,无奈此生当为程序员',
videoUrl: 'https://vd2.bdstatic.com/mda-pj8qa65bc9r1v1cf/576p/h264/1696871444324088416/mda-pj8qa65bc9r1v1cf.mp4?auth_key=1703155654-0-0-fdc0ca9c37ec26be3da9809b89e6151c&bcevod_channel=searchbox_feed&pd=1&cr=1&cd=0&pt=4&logid=2854467125&vid=5483608480722064830&abtest=all'
},
{
title: '当你阅读到这段文字的时候,我早已入睡,当我在睡梦中惊醒,你却早已安然睡去',
videoUrl: 'https://vd2.bdstatic.com/mda-pmexhyfui3e6rbmd/hd/cae_h264/1702705379314308540/mda-pmexhyfui3e6rbmd.mp4?auth_key=1703155684-0-0-5b0145fb4c2ec2f0d1bbd525ddb3d592&bcevod_channel=searchbox_feed&pd=1&cr=1&cd=0&pt=4&logid=2884962294&vid=3059586091403538183&abtest=all'
},
{
title: '每个人的内心都有一段独处的幽闭,不可诉说的窒息感孤独感在每当我们沉静下来的时候变愈发强烈',
videoUrl: 'https://vd3.bdstatic.com/mda-pmbgjjpkihkf7tjd/576p/h264/1702381478247675613/mda-pmbgjjpkihkf7tjd.mp4?v_from_s=hkapp-haokan-hbf&auth_key=1703155722-0-0-ea3c2453fbbb2cca66b12e9afe3d419f&bcevod_channel=searchbox_feed&pd=1&cr=1&cd=0&pt=4&logid=2922207105&vid=9050628586030215591&abtest=all'
},
{
title: '如果在未来的某一天,某一个早晨 晚上 瞬间,你会偶然想起多年前的一段往事,其实并不是我们有多怀旧,只是因为我们走过了太多的路',
videoUrl: 'https://vd2.bdstatic.com/mda-pj7ktq9euqchetdc/cae_h264/1696824500894354779/mda-pj7ktq9euqchetdc.mp4?v_from_s=hkapp-haokan-hbf&auth_key=1703155751-0-0-fccb0f110a3b447af67eb0feeabf06ad&bcevod_channel=searchbox_feed&pd=1&cr=0&cd=0&pt=4&logid=2951492012&vid=12162674818438199896'
},
{
title: '什么是知己,有个网红说,当你理解所有人的时候,你一定不能被所有人理解,每个人都或多或少的自私,只是或多或少而已',
videoUrl: 'https://vd3.bdstatic.com/mda-pmh5hr95fg6u8u0k/hd/cae_h264/1702877143957184120/mda-pmh5hr95fg6u8u0k.mp4?v_from_s=hkapp-haokan-hbf&auth_key=1703155785-0-0-5cfc2be95d00306082c7875a747dd998&bcevod_channel=searchbox_feed&pd=1&cr=1&cd=0&pt=4&logid=2985314718&vid=2720370579167170031&abtest=all'
}
]
4.3 绘画能力-画布组件
- ArkUI里面的画布和前端的Canvas的用法基本一致
- 使用方法
1. 放置Canvas组件-给宽和高
2. 初始化画笔对象 CanvasRenderingContext2D,将画笔对象作为构造参数传递给Canvas组件
3. 可以在Canvas的onReady事件中进行动态绘制
4. 绘制方法官方文档
- 了解绘画的基本条件:画布、画笔、绘制方法
@Entry
@Component
struct CanvasCase {
// 2.准备一根笔,传入画布
myPen: CanvasRenderingContext2D = new CanvasRenderingContext2D()
drawLine() {
// moveTo:笔离开画布移动
this.myPen.moveTo(0, 0)
// moveTo:笔在画布移动
this.myPen.lineTo(100, 100)
// 线宽
this.myPen.lineWidth = 4
// 线条颜色
this.myPen.strokeStyle = 'red'
// 绘制
this.myPen.stroke()
}
build() {
Row() {
Column() {
// 1.准备一个画布
Canvas(this.myPen)
.width('100%')
.height(300)
.backgroundColor(Color.Gray)
.onReady(() => {
// 3.准备好后就可以进行绘画了
this.drawLine()
})
}
.width('100%')
}
.height('100%')
}
}
绘制其他内容

@Entry
@Component
struct CanvasCase2 {
// 2.准备一根笔,传入画布
myPen: CanvasRenderingContext2D = new CanvasRenderingContext2D()
@State
canvasWidth: number = 0
@State
canvasHeight: number = 0
// 清空画布
drawClear() {
this.myPen.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
}
// 画线
drawLine() {
this.myPen.beginPath()
// moveTo:笔离开画布移动
this.myPen.moveTo(0, 0)
// moveTo:笔在画布移动
this.myPen.lineTo(100, 100)
// 线宽
this.myPen.lineWidth = 4
// 线条颜色
this.myPen.strokeStyle = 'red'
// 绘制
this.myPen.stroke()
this.myPen.closePath()
}
// 画圆
drawCircle() {
this.myPen.beginPath()
this.myPen.lineWidth = 2
this.myPen.arc(this.canvasWidth / 2, this.canvasHeight / 2, 100, 0, 360)
this.myPen.stroke()
this.myPen.closePath()
}
// 画矩形
drawRect() {
this.myPen.beginPath()
this.myPen.lineWidth = 2
this.myPen.strokeRect(50, 50, 100, 80)
// 实心
// this.myPen.fillRect(50,50,100,80)
this.myPen.closePath()
}
// 画贝塞尔曲线
drawBezierCurve() {
this.myPen.beginPath()
this.myPen.lineWidth = 2
this.myPen.moveTo(50, 50)
this.myPen.bezierCurveTo(100, 233, 30, 327, 111, 343)
this.myPen.stroke()
this.myPen.closePath()
}
// 画文字
drawText(){
this.myPen.beginPath()
this.myPen.font = '100px sans-serif '
this.myPen.fillText('精忠报国',this.canvasWidth/2,this.canvasHeight/2)
this.myPen.closePath()
}
//画图
drawImage(){
this.myPen.beginPath()
this.myPen.drawImage(new ImageBitmap('/assets/1.webp'),0,0)
this.myPen.closePath()
}
build() {
Column({ space: 15 }) {
// 1.准备一个画布
Canvas(this.myPen)
.width('100%')
.height(580)
.backgroundColor(Color.Gray)
.onReady(() => {
// 3.准备好后就可以进行绘画了
// this.drawLine()
})
.onAreaChange((_, _val) => {
this.canvasWidth = _val.width as number
this.canvasHeight = _val.height as number
})
Flex({ wrap: FlexWrap.Wrap }) {
Button('清空')
.onClick(() => {
this.drawClear()
})
Button('画线')
.onClick(() => {
this.drawLine()
})
Button('画圆')
.onClick(() => {
this.drawCircle()
})
Button('画矩形')
.onClick(() => {
this.drawRect()
})
Button('画曲线')
.onClick(() => {
this.drawBezierCurve()
})
Button('画文字')
.onClick(() => {
this.drawText()
})
Button('画图')
.onClick(() => {
this.drawImage()
})
}.width('100%')
}
.width('100%')
.height('100%')
}
}
4.4 签字版

- 接下来需要处理什么时候开始在画板上画的时机问题了
- Canvas有一个onTouch事件, 里面包含 按下,抬起,移动等事件,我们认为按下,表示开始画,抬起表示动作结束,移动表示正式绘制,尝试用事件来测试一下
Canvas(this.context)
.width(360)
.height(300)
.backgroundColor(Color.Pink)
.onTouch((event: TouchEvent) => {
if(event.type === TouchType.Down) {
promptAction.showToast({ message: '开始绘画' })
}
if(event.type === TouchType.Move) {
promptAction.showToast({ message: '绘画中' })
}
if(event.type === TouchType.Up) {
promptAction.showToast({ message: '结束绘画' })
}
})
- 实现绘画
.onReady(() => {
this.myPen.lineWidth = 2
this.myPen.strokeStyle = 'red'
})
.onTouch((event) => {
if (event.type === TouchType.Down) {
this.myPen.beginPath()
this.myPen.moveTo(event.touches[0].x, event.touches[0].y)
} else if (event.type === TouchType.Move) {
this.myPen.lineTo(event.touches[0].x, event.touches[0].y)
this.myPen.stroke()
} else if (event.type === TouchType.Up) {
this.myPen.closePath()
}
})
实现保存图片和清空画布方法,画布的高度需要使用onAreaChange的方式来获取
.onAreaChange((oldArea, newArea) => {
this.canvasWidth = newArea.width as number
this.canvasHeight = newArea.height as number
})
- 按钮调用对应的方法
Row () {
Button("清空画布")
.onClick(() => {
this.clearCanvas()
})
Button("保存图片")
.onClick(() => {
this.savePicture()
})
}
- 清屏方法
@State
canvasWidth: number = 0
@State
canvasHeight: number = 0
// 清空画布
drawClear() {
this.myPen.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
}
- 存储图片
存储图片是将canvas转成base64,可以直接用于展示
Button("存储图片")
.onClick(() => {
this.imageUrl = this.context.toDataURL("image/jpg")
})
也可以将图片写入沙箱后展示,需要将base64 -> buffer

Button("存储图片")
.onClick(() => {
// 使用下载到沙箱的图片
let img = this.myPen.toDataURL('image/jpg')
const filePath = getContext().tempDir + "/" + Date.now() + '.jpeg'
const file = fileIo.openSync(filePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE)
const base64Image = img.split(';base64,').pop();
// 将base64数据解码为二进制数据
const imgBuffer = buffer.from(base64Image, "base64")
fileIo.writeSync(file.fd, imgBuffer.buffer)
fileIo.closeSync(file)
this.imageUrl = "file://" + filePath
})
如果希望手势移动渲染的更加丝滑,可以给画笔加上抗锯齿处理
context: CanvasRenderingContext2D = new CanvasRenderingContext2D(new RenderingContextSettings(true))
- 完整代码
import { fileIo } from '@kit.CoreFileKit'
import { buffer } from '@kit.ArkTS'
@Entry
@Component
struct SignBoardCase {
myPen: CanvasRenderingContext2D = new CanvasRenderingContext2D(new RenderingContextSettings(true))
@State
canvasWidth: number = 0
@State
canvasHeight: number = 0
@State
imageUrl:string = ''
// 清空画布
drawClear() {
this.myPen.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
}
build() {
Row() {
Column({ space: 20 }) {
Text('签字板')
Canvas(this.myPen)
.width('100%')
.height(300)
.backgroundColor(Color.Pink)
.onReady(() => {
this.myPen.lineWidth = 2
this.myPen.strokeStyle = 'red'
})
.onAreaChange((_, _val) => {
this.canvasWidth = _val.width as number
this.canvasHeight = _val.height as number
})
.onTouch((event) => {
if (event.type === TouchType.Down) {
this.myPen.beginPath()
this.myPen.moveTo(event.touches[0].x, event.touches[0].y)
} else if (event.type === TouchType.Move) {
this.myPen.lineTo(event.touches[0].x, event.touches[0].y)
this.myPen.stroke()
} else if (event.type === TouchType.Up) {
this.myPen.closePath()
}
})
if(this.imageUrl){
Image(this.imageUrl)
.width('100%')
}
Row({ space: 20 }) {
Button('保存')
.onClick(() => {
// 使用canvas转化的图片
// this.imageUrl = this.myPen.toDataURL('image/jpg')
// 使用下载到沙箱的图片
let img = this.myPen.toDataURL('image/jpg')
const filePath = getContext().tempDir + "/" + Date.now() + '.jpeg'
const file = fileIo.openSync(filePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE)
const base64Image = img.split(';base64,').pop();
// 将base64数据解码为二进制数据
const imgBuffer = buffer.from(base64Image, "base64")
fileIo.writeSync(file.fd, imgBuffer.buffer)
fileIo.closeSync(file)
this.imageUrl = "file://" + filePath
})
Button('重签')
.onClick(() => {
this.drawClear()
this.imageUrl = ''
})
}
}
.width('100%')
}
.height('100%')
}
}
HarmonyOS赋能资源丰富度建设(第四期)-吴东林
更多推荐



所有评论(0)