
鸿蒙Next版聊天app实例|ArkTs+ArkUI仿微信
历经一个月有余爆肝高强度开发,原创重磅新作HarmonyOS 5.0 api12聊天app项目正式完结了。
HarmonyOS-Next5.0-API12仿微信聊天App应用
项目框架目录
HarmonyOS-Chat聊天app已经同步到我的原创作品集。
想要更快进阶鸿蒙开发,先把官方文档撸一遍,然后找个实战项目学习。
鸿蒙os开发者官网
https://developer.huawei.com/consumer/cn/
路由页面json文件
arkts/arkui封装自定义导航栏
之前有写过一篇专门的分享介绍,感兴趣的可以去看看下面这篇文章。
https://www.cnblogs.com/xiaoyan2017/p/18517517
arkts/arkui登录模板/倒计时
/**
* 登录模板
* @author andy
*/
import { router, promptAction } from '@kit.ArkUI'
@Entry
@Component
struct Login {
@State name: string = ''
@State pwd: string = ''
// 提交
handleSubmit() {
if(this.name === '' || this.pwd === '') {
promptAction.showToast({ message: '账号或密码不能为空' })
}else {
// 登录接口逻辑...
promptAction.showToast({ message: '登录成功' })
setTimeout(() => {
router.replaceUrl({ url: 'pages/Index' })
}, 2000)
}
}
build() {
Column() {
Column({space: 10}) {
Image('pages/assets/images/logo.png').height(50).width(50)
Text('HarmonyOS-Chat').fontSize(18).fontColor('#0a59f7')
}
.margin({top: 50})
Column({space: 15}) {
TextInput({placeholder: '请输入账号'})
.onChange((value) => {
this.name = value
})
TextInput({placeholder: '请输入密码'}).type(InputType.Password)
.onChange((value) => {
this.pwd = value
})
Button('登录').height(45).width('100%')
.linearGradient({ angle: 135, colors: [['#0a59f7', 0.1], ['#07c160', 1]] })
.onClick(() => {
this.handleSubmit()
})
}
.margin({top: 30})
.width('80%')
Row({space: 15}) {
Text('忘记密码').fontSize(14).opacity(0.5)
Text('注册账号').fontSize(14).opacity(0.5)
.onClick(() => {
router.pushUrl({url: 'pages/views/auth/Register'})
})
}
.margin({top: 20})
}
.height('100%')
.width('100%')
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
}
}
60s倒计时功能
Stack({alignContent: Alignment.End}) {
TextInput({placeholder: '验证码'})
.onChange((value) => {
this.code = value
})
Button(`${this.codeText}`).enabled(!this.disabled).controlSize(ControlSize.SMALL).margin({right: 5})
.onClick(() => {
this.handleVCode()
})
}
// 验证码参数
@State codeText: string = '获取验证码'
@State disabled: boolean = false
@State time: number = 60
// 获取验证码
handleVCode() {
if(this.tel === '') {
promptAction.showToast({ message: '请输入手机号' })
}else if(!checkMobile(this.tel)) {
promptAction.showToast({ message: '手机号格式错误' })
}else {
const timer = setInterval(() => {
if(this.time > 0) {
this.disabled = true
this.codeText = `获取验证码(${this.time--})`
}else {
clearInterval(timer)
this.codeText = '获取验证码'
this.time = 5
this.disabled = false
}
}, 1000)
}
}
harmony-chat聊天app项目涉及知识点很多,对于想要快速入门到进阶开发HarmonyOS应用的开发者,建议先阅读官方文档,然后再找一个实战项目案例进行练习。华为官网提供了HarmonyOS开发设计规范和ArkUI方舟UI框架的相关资料,这些都是宝贵的开发资源
作者:xiaoyan2017
链接: https://segmentfault.com/a/1190000045487385
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
历经一个月有余爆肝高强度开发,原创重磅新作HarmonyOS 5.0 api12聊天app项目正式完结了。
HarmonyOS-Next5.0-API12仿微信聊天App应用
项目框架目录
HarmonyOS-Chat聊天app已经同步到我的原创作品集。
想要更快进阶鸿蒙开发,先把官方文档撸一遍,然后找个实战项目学习。
鸿蒙os开发者官网
https://developer.huawei.com/consumer/cn/
路由页面json文件
arkts/arkui封装自定义导航栏
之前有写过一篇专门的分享介绍,感兴趣的可以去看看下面这篇文章。
https://www.cnblogs.com/xiaoyan2017/p/18517517
arkts/arkui登录模板/倒计时
/**
* 登录模板
* @author andy
*/
import { router, promptAction } from '@kit.ArkUI'
@Entry
@Component
struct Login {
@State name: string = ''
@State pwd: string = ''
// 提交
handleSubmit() {
if(this.name === '' || this.pwd === '') {
promptAction.showToast({ message: '账号或密码不能为空' })
}else {
// 登录接口逻辑...
promptAction.showToast({ message: '登录成功' })
setTimeout(() => {
router.replaceUrl({ url: 'pages/Index' })
}, 2000)
}
}
build() {
Column() {
Column({space: 10}) {
Image('pages/assets/images/logo.png').height(50).width(50)
Text('HarmonyOS-Chat').fontSize(18).fontColor('#0a59f7')
}
.margin({top: 50})
Column({space: 15}) {
TextInput({placeholder: '请输入账号'})
.onChange((value) => {
this.name = value
})
TextInput({placeholder: '请输入密码'}).type(InputType.Password)
.onChange((value) => {
this.pwd = value
})
Button('登录').height(45).width('100%')
.linearGradient({ angle: 135, colors: [['#0a59f7', 0.1], ['#07c160', 1]] })
.onClick(() => {
this.handleSubmit()
})
}
.margin({top: 30})
.width('80%')
Row({space: 15}) {
Text('忘记密码').fontSize(14).opacity(0.5)
Text('注册账号').fontSize(14).opacity(0.5)
.onClick(() => {
router.pushUrl({url: 'pages/views/auth/Register'})
})
}
.margin({top: 20})
}
.height('100%')
.width('100%')
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
}
}
60s倒计时功能
Stack({alignContent: Alignment.End}) {
TextInput({placeholder: '验证码'})
.onChange((value) => {
this.code = value
})
Button(`${this.codeText}`).enabled(!this.disabled).controlSize(ControlSize.SMALL).margin({right: 5})
.onClick(() => {
this.handleVCode()
})
}
// 验证码参数
@State codeText: string = '获取验证码'
@State disabled: boolean = false
@State time: number = 60
// 获取验证码
handleVCode() {
if(this.tel === '') {
promptAction.showToast({ message: '请输入手机号' })
}else if(!checkMobile(this.tel)) {
promptAction.showToast({ message: '手机号格式错误' })
}else {
const timer = setInterval(() => {
if(this.time > 0) {
this.disabled = true
this.codeText = `获取验证码(${this.time--})`
}else {
clearInterval(timer)
this.codeText = '获取验证码'
this.time = 5
this.disabled = false
}
}, 1000)
}
}
harmony-chat聊天app项目涉及知识点很多,对于想要快速入门到进阶开发HarmonyOS应用的开发者,建议先阅读官方文档,然后再找一个实战项目案例进行练习。华为官网提供了HarmonyOS开发设计规范和ArkUI方舟UI框架的相关资料,这些都是宝贵的开发资源
作者:xiaoyan2017
链接: https://segmentfault.com/a/1190000045487385
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

建议:HBuilderX全局书签功能
建议:HBuilderX全局书签功能
HBuilderX书签,当前好像只能从本本文档内有效?
实际开发时,经常需要几个文档的指定位置分别编辑。能否定义全局书签,可直接跳转到另一个文档的书签位置?
建议:HBuilderX全局书签功能
HBuilderX书签,当前好像只能从本本文档内有效?
实际开发时,经常需要几个文档的指定位置分别编辑。能否定义全局书签,可直接跳转到另一个文档的书签位置?

真机运行跳转正常,但是“打包和上线”都失败报错bad_param
uniapp开发的app 跳转微信小程序 我在manifest.json分享权限配置的appid 是微信开放平台移动应用的appid
使用的是5+launchMiniProgram ID为小程序的原始id
真机运行跳转正常,但是“打包和上线”都失败报错bad_param
下面是代码块
const originId = 'gh_1ae' // 微信小程序原始id
const path = PalyUrl.value // 小程序页面路径
const envVersion = import.meta.env.VITE_APP_VERSION // 0-正式版; 1-测试版; 2-体验版
const webUrl = 'https://www.dcloud.io/hbuilderx.html' // 兼容低版本的网页链接
plus.share.getServices(service => {
const weixin = service.find(i => i.id === 'weixin')
if (weixin) {
weixin.launchMiniProgram({
id: originId,
path,
type: envVersion,
webUrl
}, res => {
console.log('res', res)
}, err => {
console.log('err', err)
})
uniapp开发的app 跳转微信小程序 我在manifest.json分享权限配置的appid 是微信开放平台移动应用的appid
使用的是5+launchMiniProgram ID为小程序的原始id
真机运行跳转正常,但是“打包和上线”都失败报错bad_param
下面是代码块
const originId = 'gh_1ae' // 微信小程序原始id
const path = PalyUrl.value // 小程序页面路径
const envVersion = import.meta.env.VITE_APP_VERSION // 0-正式版; 1-测试版; 2-体验版
const webUrl = 'https://www.dcloud.io/hbuilderx.html' // 兼容低版本的网页链接
plus.share.getServices(service => {
const weixin = service.find(i => i.id === 'weixin')
if (weixin) {
weixin.launchMiniProgram({
id: originId,
path,
type: envVersion,
webUrl
}, res => {
console.log('res', res)
}, err => {
console.log('err', err)
})
收起阅读 »

微信公众号分享,永远是链接不是卡片模式的原因
做的 H5 的项目,分享所有的测试都正常。微信开发者工具也没有报错, 在真实线上也没有报错。但分享出来都是链接。
有几点要注意。
- 分享的链接,在 js-sdk 中配置的分享链接,必须和当前的网页一样, 不能分享其它安全配置域名的其它链接。
- 微信开放全域名访问后出现的限制。只能是 公众号菜单进入分享、扫描二维码后分享、添加到收藏,从微信我的收藏进入分享 这几种情况是正常的。其他地方进入分享都是链接。这个没有任何公开的文档提到这个。巨坑之一。TMD 微信。
做的 H5 的项目,分享所有的测试都正常。微信开发者工具也没有报错, 在真实线上也没有报错。但分享出来都是链接。
有几点要注意。
- 分享的链接,在 js-sdk 中配置的分享链接,必须和当前的网页一样, 不能分享其它安全配置域名的其它链接。
- 微信开放全域名访问后出现的限制。只能是 公众号菜单进入分享、扫描二维码后分享、添加到收藏,从微信我的收藏进入分享 这几种情况是正常的。其他地方进入分享都是链接。这个没有任何公开的文档提到这个。巨坑之一。TMD 微信。

uni.login 你所不知道的坑
使用uni.login调用微信登录小程序、安卓App一路都很顺畅,但到了IOS,出现一个奇葩的事件,先来看,就是一段简单的登录代码
uni.login({
provider: 'weixin',
onlyAuthorize: true,
success: (res2) => {
console.log('getUserProfile:------4.')
},
fail: (res) => {
console.log('loginWechat.fail:', res)
},
complete:(res)=>{
console.log('..................')
}
});
在自定义基座的情况,如果你是在HbuilderX中调用基座执行,那么你会发现,无论任何情况,上面的代码执行都不会有问题。但如果你切换到Xcode中执行,那么问题来了,你首次调用uni.login时,也能正常返回,但如果第二次调用(30秒内调用),你会发现不管是success、fail、complete,三者不会有任何输出,也就是uni.login调用失败。那么,如何再次成功调用呢?答案是等30秒,为啥要等30秒呢?在30秒后,在Xcode中会输出一段警告日志,如下:
Background Task 3 ("Called by HBuilder, from -[AppDelegate applicationDidEnterBackground:]"), was created over 30 seconds ago. In applications running in the background, this creates a risk of termination. Remember to call UIApplication.endBackgroundTask(_:) for your task in a timely manner to avoid this.
你只能看到该日志输出后,你再次调用uni.login才会正常,我非专业的IOS开发,大体了解了一下,也就是说SDK中调用beginBackgroundTask,没有调用 endBackgroundTask,这个BUG有什么好的解决方法呢?还是说我哪没配置对?
使用uni.login调用微信登录小程序、安卓App一路都很顺畅,但到了IOS,出现一个奇葩的事件,先来看,就是一段简单的登录代码
uni.login({
provider: 'weixin',
onlyAuthorize: true,
success: (res2) => {
console.log('getUserProfile:------4.')
},
fail: (res) => {
console.log('loginWechat.fail:', res)
},
complete:(res)=>{
console.log('..................')
}
});
在自定义基座的情况,如果你是在HbuilderX中调用基座执行,那么你会发现,无论任何情况,上面的代码执行都不会有问题。但如果你切换到Xcode中执行,那么问题来了,你首次调用uni.login时,也能正常返回,但如果第二次调用(30秒内调用),你会发现不管是success、fail、complete,三者不会有任何输出,也就是uni.login调用失败。那么,如何再次成功调用呢?答案是等30秒,为啥要等30秒呢?在30秒后,在Xcode中会输出一段警告日志,如下:
Background Task 3 ("Called by HBuilder, from -[AppDelegate applicationDidEnterBackground:]"), was created over 30 seconds ago. In applications running in the background, this creates a risk of termination. Remember to call UIApplication.endBackgroundTask(_:) for your task in a timely manner to avoid this.
你只能看到该日志输出后,你再次调用uni.login才会正常,我非专业的IOS开发,大体了解了一下,也就是说SDK中调用beginBackgroundTask,没有调用 endBackgroundTask,这个BUG有什么好的解决方法呢?还是说我哪没配置对?
收起阅读 »
uni-registerRequestPermissionTips插件的ts类型定义
由于在基于ts开发项目中直接引用uts插件会出现类型报错问题,但插件实际是正常的,所以这里提供一份uni-registerRequestPermissionTips的类型声明文件,解决类型报错问题。
文件名可以起名为uni-registerRequestPermissionTips.d.ts,丢到项目公共的类型目录即可。
declare module '@/uni_modules/uni-registerRequestPermissionTips'{
export interface RequestPermissionTipsListener {
/**
* 申请系统权限回调,permissions为触发权限申请的所有权限
*/
onRequest?: ((permissions: Array<'grant' | 'denied'>) => void) | null
/**
* 弹出系统权限授权框回调,permissions为触发弹出权限授权框的所有权限
*/
onConfirm?: ((permission: Array<'grant' | 'denied'>) => void) | null
/**
* 权限申请完成回调,permissions包括权限及权限的状态。
*
* grant为权限已获取,denied为权限已拒绝
*/
onComplete?: ((permissions: Record<string, 'grant' | 'denied'>) => void) | null
}
export type RegisterRequestPermissionTipsListener = (listener: RequestPermissionTipsListener | null) => void
export type UnregisterRequestPermissionTipsListener = (listener: RequestPermissionTipsListener | null) => void
export type SetRequestPermissionTips = (tips: Record<string, string>) => void
/**
* 注册权限监听器。
*
* @see https://ext.dcloud.net.cn/plugin?name=uni-registerRequestPermissionTips
*/
export const registerRequestPermissionTipsListener: RegisterRequestPermissionTipsListener
/**
* 设置权限监听的说明。支持针对权限设置具体的说明。
*
* @see https://ext.dcloud.net.cn/plugin?name=uni-registerRequestPermissionTips
*/
export const setRequestPermissionTips: SetRequestPermissionTips
/**
* 注销权限监听器。
*
* @see https://ext.dcloud.net.cn/plugin?name=uni-registerRequestPermissionTips
*
*/
export const unregisterRequestPermissionTipsListener: UnregisterRequestPermissionTipsListener
}
由于在基于ts开发项目中直接引用uts插件会出现类型报错问题,但插件实际是正常的,所以这里提供一份uni-registerRequestPermissionTips的类型声明文件,解决类型报错问题。
文件名可以起名为uni-registerRequestPermissionTips.d.ts,丢到项目公共的类型目录即可。
declare module '@/uni_modules/uni-registerRequestPermissionTips'{
export interface RequestPermissionTipsListener {
/**
* 申请系统权限回调,permissions为触发权限申请的所有权限
*/
onRequest?: ((permissions: Array<'grant' | 'denied'>) => void) | null
/**
* 弹出系统权限授权框回调,permissions为触发弹出权限授权框的所有权限
*/
onConfirm?: ((permission: Array<'grant' | 'denied'>) => void) | null
/**
* 权限申请完成回调,permissions包括权限及权限的状态。
*
* grant为权限已获取,denied为权限已拒绝
*/
onComplete?: ((permissions: Record<string, 'grant' | 'denied'>) => void) | null
}
export type RegisterRequestPermissionTipsListener = (listener: RequestPermissionTipsListener | null) => void
export type UnregisterRequestPermissionTipsListener = (listener: RequestPermissionTipsListener | null) => void
export type SetRequestPermissionTips = (tips: Record<string, string>) => void
/**
* 注册权限监听器。
*
* @see https://ext.dcloud.net.cn/plugin?name=uni-registerRequestPermissionTips
*/
export const registerRequestPermissionTipsListener: RegisterRequestPermissionTipsListener
/**
* 设置权限监听的说明。支持针对权限设置具体的说明。
*
* @see https://ext.dcloud.net.cn/plugin?name=uni-registerRequestPermissionTips
*/
export const setRequestPermissionTips: SetRequestPermissionTips
/**
* 注销权限监听器。
*
* @see https://ext.dcloud.net.cn/plugin?name=uni-registerRequestPermissionTips
*
*/
export const unregisterRequestPermissionTipsListener: UnregisterRequestPermissionTipsListener
}
收起阅读 »

没有十年脑血栓做不出来这种跨平台设计
<template>
<view>
<image ref="logo" class="logo" src="/static/logo.png"></image>
<text>{{cxtInfo}}</text>
<text>{{text}}</text>
<view class="center">
<canvas id="canvas" class="center" style="width: 200px;height: 200px;border: 1px solid red;" ></canvas>
</view>
<button @click="draw()">测试</button>
</view>
</template>
<script>
export default {
data() {
return {
cxt : null as CanvasRenderingContext2D | null,
cxtInfo:{},
text:"",
title: 'Hello'
}
},
onReady() {
console.info("onReady")
uni.createCanvasContextAsync({
id: 'canvas',
component: getCurrentInstance()?.proxy,
success: (context : CanvasContext) => {
const canvasContext = context.getContext('2d')!;
const canvas = canvasContext.canvas;
let {offsetWidth,offsetHeight} = canvas;
// 处理高清屏逻辑
const dpr = uni.getDeviceInfo().devicePixelRatio ?? 1;
this.cxtInfo = {offsetWidth,offsetHeight,dpr}
canvas.width = canvas.offsetWidth * dpr;
canvas.height = canvas.offsetHeight * dpr;
canvasContext.scale(dpr, dpr); // 仅需调用一次,当调用 reset 方法后需要再次 scale
this.cxt= canvasContext;
}
})
},
onLoad() {
console.info("onLoad")
},
methods: {
draw(){
const cxt =this.cxt!;
cxt.beginPath();
cxt.lineWidth = 2;
cxt.arc(75, 75, 50, 0, Math.PI * 2, true);
cxt.stroke();
let img=this.$refs["logo"] as UniImageElement;
if(img==null){
return;
}
console.info ("img ",img);
console.info ("img.attributes ",img.attributes);
console.info ("img.src ",img.src);
let attrs= img.attributes ;
console.info ("attrs.size ",attrs.size);
// #ifdef WEB
for(let i=0;i< attrs.length ; i++){
let attr = attrs[i];
console.info ("img.attributes."+attr["name"],"=", attr["value"] );
}
// #endif
// #ifdef APP
attrs.forEach((v,k)=>{ //web平台没有
console.info ("img.attributes."+k,v);
})
// #endif
console.info ("img type",typeof img);
console.info (img instanceof Image);
let lc= img.lastChild;
console.info ("lastChild is not null ",lc!=null);
let bcr=img.getBoundingClientRect();
console.info ("BoundingClientRect ",bcr);
if(img instanceof Image){//安卓
this.text="Image"
cxt.drawImage(img,50,50);
}else if(lc!=null && lc instanceof Image){//Web
this.text="Image.lastChild"
cxt.drawImage(lc,100,100);
}else{//IOS
this.text="new Image"
let image = new Image(bcr.width, bcr.height)
image.onload=( )=>{
console.info("image onload ",typeof image)
cxt.drawImage(image,0,0,100,100);
}
image.src = img.attributes.get("src") as string;
}
}
}
}
</script>
<style>
.logo {
height: 100px;
width: 100px;
margin: 100px auto 25px auto;
}
.title {
font-size: 18px;
color: #8f8f94;
text-align: center;
}
.center {
height: 100%;
flex: auto;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
</style>
UniElement Extends UniElement // {src:string ,....}
Image Extends UniElement // {src : string }
CanvasRenderingContext2D
drawImage(imageResource: Image, sx: number, sy: number): void
Image 标签(UniElement )竟然和 Image 对象毫无关系
元素属性 attributes 对象 Map<string, any> 竟然无一方式可以跨平台遍历
且Image标签的src属性在不同平台所在位置还不一样
且 对于 attributes ["src"],attributes .get("src") 两张写法竟然还不等价
lc instanceof Image
如何不在前面排除null 竟然会报错null is not an object (evaluating 'Object.getPrototypeOf(e)')
而不是直接返回false
二进制是每种语言基础中的基础 文件读取 encoding 还给限制死了 只能是 base64 utf-8 然后文档还一边说可以不写 这操作就很魔幻

基本上所有api都支持 success,fail,complete 那么所有 opt 都继承 Iopt<S,E> 不就可以统一包装支持 Promise<S,E>了吗
复杂点如果传入了 success,fail,complete 就不返回 Promise 简单就不论是否传入都返回Promise 让opt中的success,fail,complete优先回调就好了
<template>
<view>
<image ref="logo" class="logo" src="/static/logo.png"></image>
<text>{{cxtInfo}}</text>
<text>{{text}}</text>
<view class="center">
<canvas id="canvas" class="center" style="width: 200px;height: 200px;border: 1px solid red;" ></canvas>
</view>
<button @click="draw()">测试</button>
</view>
</template>
<script>
export default {
data() {
return {
cxt : null as CanvasRenderingContext2D | null,
cxtInfo:{},
text:"",
title: 'Hello'
}
},
onReady() {
console.info("onReady")
uni.createCanvasContextAsync({
id: 'canvas',
component: getCurrentInstance()?.proxy,
success: (context : CanvasContext) => {
const canvasContext = context.getContext('2d')!;
const canvas = canvasContext.canvas;
let {offsetWidth,offsetHeight} = canvas;
// 处理高清屏逻辑
const dpr = uni.getDeviceInfo().devicePixelRatio ?? 1;
this.cxtInfo = {offsetWidth,offsetHeight,dpr}
canvas.width = canvas.offsetWidth * dpr;
canvas.height = canvas.offsetHeight * dpr;
canvasContext.scale(dpr, dpr); // 仅需调用一次,当调用 reset 方法后需要再次 scale
this.cxt= canvasContext;
}
})
},
onLoad() {
console.info("onLoad")
},
methods: {
draw(){
const cxt =this.cxt!;
cxt.beginPath();
cxt.lineWidth = 2;
cxt.arc(75, 75, 50, 0, Math.PI * 2, true);
cxt.stroke();
let img=this.$refs["logo"] as UniImageElement;
if(img==null){
return;
}
console.info ("img ",img);
console.info ("img.attributes ",img.attributes);
console.info ("img.src ",img.src);
let attrs= img.attributes ;
console.info ("attrs.size ",attrs.size);
// #ifdef WEB
for(let i=0;i< attrs.length ; i++){
let attr = attrs[i];
console.info ("img.attributes."+attr["name"],"=", attr["value"] );
}
// #endif
// #ifdef APP
attrs.forEach((v,k)=>{ //web平台没有
console.info ("img.attributes."+k,v);
})
// #endif
console.info ("img type",typeof img);
console.info (img instanceof Image);
let lc= img.lastChild;
console.info ("lastChild is not null ",lc!=null);
let bcr=img.getBoundingClientRect();
console.info ("BoundingClientRect ",bcr);
if(img instanceof Image){//安卓
this.text="Image"
cxt.drawImage(img,50,50);
}else if(lc!=null && lc instanceof Image){//Web
this.text="Image.lastChild"
cxt.drawImage(lc,100,100);
}else{//IOS
this.text="new Image"
let image = new Image(bcr.width, bcr.height)
image.onload=( )=>{
console.info("image onload ",typeof image)
cxt.drawImage(image,0,0,100,100);
}
image.src = img.attributes.get("src") as string;
}
}
}
}
</script>
<style>
.logo {
height: 100px;
width: 100px;
margin: 100px auto 25px auto;
}
.title {
font-size: 18px;
color: #8f8f94;
text-align: center;
}
.center {
height: 100%;
flex: auto;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
</style>
UniElement Extends UniElement // {src:string ,....}
Image Extends UniElement // {src : string }
CanvasRenderingContext2D
drawImage(imageResource: Image, sx: number, sy: number): void
Image 标签(UniElement )竟然和 Image 对象毫无关系
元素属性 attributes 对象 Map<string, any> 竟然无一方式可以跨平台遍历
且Image标签的src属性在不同平台所在位置还不一样
且 对于 attributes ["src"],attributes .get("src") 两张写法竟然还不等价
lc instanceof Image
如何不在前面排除null 竟然会报错null is not an object (evaluating 'Object.getPrototypeOf(e)')
而不是直接返回false
二进制是每种语言基础中的基础 文件读取 encoding 还给限制死了 只能是 base64 utf-8 然后文档还一边说可以不写 这操作就很魔幻
基本上所有api都支持 success,fail,complete 那么所有 opt 都继承 Iopt<S,E> 不就可以统一包装支持 Promise<S,E>了吗
复杂点如果传入了 success,fail,complete 就不返回 Promise 简单就不论是否传入都返回Promise 让opt中的success,fail,complete优先回调就好了
收起阅读 »
发布鸿蒙App,领取现金奖励
> 如果你的应用用户体量较大,比如日活超过1万,欢迎联系我们,报名参加更高激励活动,若审核成功,奖励金额至少2100元起。
为了丰富鸿蒙生态,为国产操作系统生态做贡献,DCloud联合华为推出开发者激励计划,uni-app开发者只需将 uni-app 项目发行为鸿蒙App,即可获得现金激励,欢迎各位开发者积极提交。
激励规则
根据应用是否在华为应用市场,或者其他手机终端厂商(仅指荣耀、小米、OPPO、vivo、Apple)应用市场、小程序平台(微信、支付宝、抖音、小红书)面向中国境内(不含中国香港、中国澳门、中国台湾)上架发布,将应用分为以下两类:
- 成熟应用:在2024年9月30日前(含当日)在上述应用分发平台为在架状态;
- 新应用:新开发的应用,在2024年9月30日前(含当日)未在上述应用分发平台上架;
说明:
- 成熟应用的名称需要与此前在上述应用分发平台上架的应用名称一样。
- 按照华为的政策,应用市场仅包含如上华为、荣耀、小米、OPPO、vivo、Apple几家应用市场,不含应用宝、360等三方应用商店;
- 小程序平台仅含微信、支付宝、抖音、小红书四家,不含百度、京东等;
成熟应用
评选规则:
- 2024年12月31日之前,完成鸿蒙App上架评审并正式上架;
奖励规则:
新应用
评选规则:
- 2024年12月31日之前,完成鸿蒙App上架评审并正式上架;
- 应用完成上架后次日起7日累计活跃设备数达到100;因为华为的评审截止日为12月31日,故建议在2024年12月23日之前完成上架;
奖励规则:
- 同时满足如上两个评选条件,即可获得1000元现金奖励,以及价值1万元的流量扶持赠送金;
- 同时满足如上两个评选条件,且通过uni-pay对接华为支付,或通过uni-id对接华为登录,额外奖励500元现金;
参与细则
1、开发者需将DCloud绑定为鸿蒙商店服务商,并在线签订激励分享承诺函,详见元服务发行与上架;
2、激励发放前,DCloud及华为有权再次核实计划申请人资格和相关信息。开发者需保证提交信息真实有效,若存在欺诈或不当行为(如恶意刷量、提供虚假APP备案证明等作弊行为、侵犯他人知识产权,或者存在任何违法违规或违反本计划协议的行为),DCloud有权追回已发放激励等权益,取消参与本计划资格,并追究其法律责任;
3、开发者应确保提供其真实有效的收款账号信息,若其自身原因(包括但不限于账号注销、被冻结、无法收款等)导致激励发放失败,由此引发的后果和损失由开发者自行承担;
4、现金激励为含税金额;
5、为了确保活动的公平性和正向激励导向,DCloud在法律允许范围内可优化本计划规则,请您以最新发布的规则为准。
6、如您对本计划的规则有任何疑问,请通过uni-im与我们联系。
> 如果你的应用用户体量较大,比如日活超过1万,欢迎联系我们,报名参加更高激励活动,若审核成功,奖励金额至少2100元起。
为了丰富鸿蒙生态,为国产操作系统生态做贡献,DCloud联合华为推出开发者激励计划,uni-app开发者只需将 uni-app 项目发行为鸿蒙App,即可获得现金激励,欢迎各位开发者积极提交。
激励规则
根据应用是否在华为应用市场,或者其他手机终端厂商(仅指荣耀、小米、OPPO、vivo、Apple)应用市场、小程序平台(微信、支付宝、抖音、小红书)面向中国境内(不含中国香港、中国澳门、中国台湾)上架发布,将应用分为以下两类:
- 成熟应用:在2024年9月30日前(含当日)在上述应用分发平台为在架状态;
- 新应用:新开发的应用,在2024年9月30日前(含当日)未在上述应用分发平台上架;
说明:
- 成熟应用的名称需要与此前在上述应用分发平台上架的应用名称一样。
- 按照华为的政策,应用市场仅包含如上华为、荣耀、小米、OPPO、vivo、Apple几家应用市场,不含应用宝、360等三方应用商店;
- 小程序平台仅含微信、支付宝、抖音、小红书四家,不含百度、京东等;
成熟应用
评选规则:
- 2024年12月31日之前,完成鸿蒙App上架评审并正式上架;
奖励规则:
新应用
评选规则:
- 2024年12月31日之前,完成鸿蒙App上架评审并正式上架;
- 应用完成上架后次日起7日累计活跃设备数达到100;因为华为的评审截止日为12月31日,故建议在2024年12月23日之前完成上架;
奖励规则:
- 同时满足如上两个评选条件,即可获得1000元现金奖励,以及价值1万元的流量扶持赠送金;
- 同时满足如上两个评选条件,且通过uni-pay对接华为支付,或通过uni-id对接华为登录,额外奖励500元现金;
参与细则
1、开发者需将DCloud绑定为鸿蒙商店服务商,并在线签订激励分享承诺函,详见元服务发行与上架;
2、激励发放前,DCloud及华为有权再次核实计划申请人资格和相关信息。开发者需保证提交信息真实有效,若存在欺诈或不当行为(如恶意刷量、提供虚假APP备案证明等作弊行为、侵犯他人知识产权,或者存在任何违法违规或违反本计划协议的行为),DCloud有权追回已发放激励等权益,取消参与本计划资格,并追究其法律责任;
3、开发者应确保提供其真实有效的收款账号信息,若其自身原因(包括但不限于账号注销、被冻结、无法收款等)导致激励发放失败,由此引发的后果和损失由开发者自行承担;
4、现金激励为含税金额;
5、为了确保活动的公平性和正向激励导向,DCloud在法律允许范围内可优化本计划规则,请您以最新发布的规则为准。
6、如您对本计划的规则有任何疑问,请通过uni-im与我们联系。

发布鸿蒙元服务,领取现金奖励
> 如果你是服务商、外包商,承诺上架25个元服务,欢迎点此联系我们,我们可提供单独技术支持,以及可帮助协调免费的鸿蒙测试机。
为了丰富鸿蒙生态,为国产操作系统生态做贡献,DCloud联合华为推出开发者激励计划,uni-app开发者只需将 uni-app 项目发行为鸿蒙元服务,即可获得现金激励,欢迎各位开发者积极提交。
激励规则
根据应用是否在华为应用市场,或者其他手机终端厂商(仅指荣耀、小米、OPPO、vivo、Apple)应用市场、小程序平台(微信、支付宝、抖音、小红书)面向中国境内(不含中国香港、中国澳门、中国台湾)上架发布,将应用分为以下两类:
- 成熟应用:在2024年9月30日前(含当日)在上述应用分发平台为在架状态;
- 新应用:新开发的应用,在2024年9月30日前(含当日)未在上述应用分发平台上架;
说明:
- 成熟应用的名称需要与此前在上述应用分发平台上架的应用名称一样。
- 按照华为的政策,应用市场仅包含如上华为、荣耀、小米、OPPO、vivo、Apple几家应用市场,不含应用宝、360等三方应用商店;
- 小程序平台仅含微信、支付宝、抖音、小红书四家,不含百度、京东等;
成熟应用
评选规则:
- 2024年12月31日之前,完成元服务上架评审并正式上架;
奖励规则:
- 满足如上评选条件,即可获得600元现金奖励,以及价值6000元的流量扶持赠送金;
新应用
评选规则:
- 2024年12月31日之前,完成元服务上架评审并正式上架;
- 应用完成上架后次日起7日累计活跃设备数达到100;因为华为的评审截止日为12月31日,故建议在2024年12月23日之前完成上架;
奖励规则:
- 同时满足如上两个评选条件,即可获得600元现金奖励,以及价值6000元的流量扶持赠送金;
参与细则
1、开发者需将DCloud绑定为鸿蒙商店服务商,并在线签订激励分享承诺函,详见元服务发行与上架
2、激励发放前,DCloud及华为有权再次核实计划申请人资格和相关信息。开发者需保证提交信息真实有效,若存在欺诈或不当行为(如恶意刷量、提供虚假APP备案证明等作弊行为、侵犯他人知识产权,或者存在任何违法违规或违反本计划协议的行为),DCloud有权追回已发放激励等权益,取消参与本计划资格,并追究其法律责任;
3、开发者应确保提供其真实有效的收款账号信息,若其自身原因(包括但不限于账号注销、被冻结、无法收款等)导致激励发放失败,由此引发的后果和损失由开发者自行承担;
4、现金激励为含税金额;
5、为了确保活动的公平性和正向激励导向,DCloud在法律允许范围内可优化本计划规则,请您以最新发布的规则为准。
6、如您对本计划的规则有任何疑问,请通过uni-im与我们联系。
> 如果你是服务商、外包商,承诺上架25个元服务,欢迎点此联系我们,我们可提供单独技术支持,以及可帮助协调免费的鸿蒙测试机。
为了丰富鸿蒙生态,为国产操作系统生态做贡献,DCloud联合华为推出开发者激励计划,uni-app开发者只需将 uni-app 项目发行为鸿蒙元服务,即可获得现金激励,欢迎各位开发者积极提交。
激励规则
根据应用是否在华为应用市场,或者其他手机终端厂商(仅指荣耀、小米、OPPO、vivo、Apple)应用市场、小程序平台(微信、支付宝、抖音、小红书)面向中国境内(不含中国香港、中国澳门、中国台湾)上架发布,将应用分为以下两类:
- 成熟应用:在2024年9月30日前(含当日)在上述应用分发平台为在架状态;
- 新应用:新开发的应用,在2024年9月30日前(含当日)未在上述应用分发平台上架;
说明:
- 成熟应用的名称需要与此前在上述应用分发平台上架的应用名称一样。
- 按照华为的政策,应用市场仅包含如上华为、荣耀、小米、OPPO、vivo、Apple几家应用市场,不含应用宝、360等三方应用商店;
- 小程序平台仅含微信、支付宝、抖音、小红书四家,不含百度、京东等;
成熟应用
评选规则:
- 2024年12月31日之前,完成元服务上架评审并正式上架;
奖励规则:
- 满足如上评选条件,即可获得600元现金奖励,以及价值6000元的流量扶持赠送金;
新应用
评选规则:
- 2024年12月31日之前,完成元服务上架评审并正式上架;
- 应用完成上架后次日起7日累计活跃设备数达到100;因为华为的评审截止日为12月31日,故建议在2024年12月23日之前完成上架;
奖励规则:
- 同时满足如上两个评选条件,即可获得600元现金奖励,以及价值6000元的流量扶持赠送金;
参与细则
1、开发者需将DCloud绑定为鸿蒙商店服务商,并在线签订激励分享承诺函,详见元服务发行与上架
2、激励发放前,DCloud及华为有权再次核实计划申请人资格和相关信息。开发者需保证提交信息真实有效,若存在欺诈或不当行为(如恶意刷量、提供虚假APP备案证明等作弊行为、侵犯他人知识产权,或者存在任何违法违规或违反本计划协议的行为),DCloud有权追回已发放激励等权益,取消参与本计划资格,并追究其法律责任;
3、开发者应确保提供其真实有效的收款账号信息,若其自身原因(包括但不限于账号注销、被冻结、无法收款等)导致激励发放失败,由此引发的后果和损失由开发者自行承担;
4、现金激励为含税金额;
5、为了确保活动的公平性和正向激励导向,DCloud在法律允许范围内可优化本计划规则,请您以最新发布的规则为准。
6、如您对本计划的规则有任何疑问,请通过uni-im与我们联系。

项目运行到鸿蒙手机,应用图标一直是H,应用名一直是HBuilder问题
项目运行到鸿蒙手机,应用图标一直是H,应用名一直是HBuilder问题
应用运行到鸿蒙手机和鸿蒙模拟器,应用图标一直是H,应用名一直是HBuilder,在自动生成的harmony-configs文件夹下也没有配置的文件,
- 这时候需要你将DevEco Studio 下生成的resource 文件复制一份,放在相应的文件夹下。
- 修改根据module.json5文件中的"icon": "$media:layered_image",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:startIcon",修改对应文件夹下的字符串或者图片资源。 - 重新运行到鸿蒙设备。如果运行到手机上的应用图标还是H,名字还是HBuilder。这是因为有缓存的问题。
- 找到unpackage 文件夹删除里面的debug文件夹(编译时候自动生成的)。打包鸿蒙app的时候遇到上面情况,删掉release文件夹重新运行。
就可以解决以上问题。
项目运行到鸿蒙手机,应用图标一直是H,应用名一直是HBuilder问题
应用运行到鸿蒙手机和鸿蒙模拟器,应用图标一直是H,应用名一直是HBuilder,在自动生成的harmony-configs文件夹下也没有配置的文件,
- 这时候需要你将DevEco Studio 下生成的resource 文件复制一份,放在相应的文件夹下。
- 修改根据module.json5文件中的"icon": "$media:layered_image",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:startIcon",修改对应文件夹下的字符串或者图片资源。 - 重新运行到鸿蒙设备。如果运行到手机上的应用图标还是H,名字还是HBuilder。这是因为有缓存的问题。
- 找到unpackage 文件夹删除里面的debug文件夹(编译时候自动生成的)。打包鸿蒙app的时候遇到上面情况,删掉release文件夹重新运行。
就可以解决以上问题。
收起阅读 »
uniapp 微信自动播放背景音乐插件
现在不管是 audio 还是 video(带音频) 都没办法做自动播放,受到浏览器阻止自动播放策略的约束;
还好微信内置浏览器拥有一些特殊api,可以实现自动播放。闲话少说直接看效果,微信扫一扫下面的二维码查看
其实 iOS 实现起来比较简单,只需要监听 WeixinJSBridgeReady
或者 weixin. ready
就可以正常播放 audio 元素的音频;
Android 实现起来相对麻烦一点,需要借助 soundjs 库来完成。这里我参考借鉴 superzdd 大佬的实验 wechat-h5-backgound-music-survey,有兴趣的可以去了解一下~
这里面比较麻烦的一个事情是。。。。 iOS方案和Android方案相互是不兼容的!
所以我把他封装成了一个 jssdk 插件,可以快速使用并且完美兼容微信 Android
和 iOS
以及一般手机浏览器 (触摸)
好省去一些时间浪费在做兼容这件事上,有兴趣的欢迎前来使用~ 并且项目长期维护, 有问题可以直接提交问题
现在不管是 audio 还是 video(带音频) 都没办法做自动播放,受到浏览器阻止自动播放策略的约束;
还好微信内置浏览器拥有一些特殊api,可以实现自动播放。闲话少说直接看效果,微信扫一扫下面的二维码查看
其实 iOS 实现起来比较简单,只需要监听 WeixinJSBridgeReady
或者 weixin. ready
就可以正常播放 audio 元素的音频;
Android 实现起来相对麻烦一点,需要借助 soundjs 库来完成。这里我参考借鉴 superzdd 大佬的实验 wechat-h5-backgound-music-survey,有兴趣的可以去了解一下~
这里面比较麻烦的一个事情是。。。。 iOS方案和Android方案相互是不兼容的!
所以我把他封装成了一个 jssdk 插件,可以快速使用并且完美兼容微信 Android
和 iOS
以及一般手机浏览器 (触摸)
好省去一些时间浪费在做兼容这件事上,有兴趣的欢迎前来使用~ 并且项目长期维护, 有问题可以直接提交问题
插件地址:https://ext.dcloud.net.cn/plugin?id=20174
收起阅读 »