- 微信小程序页面是否有ai工具可以生成?
想问下各位大佬,微信小程序现在的页面是否有工具可以生成,最好是能够商用的
02-23 - Skyline|小程序页面转场动画
开发者A:小程序跳转时,页面切换效果可以自定义实现吗? 开发者B:搞个单页自己写呢 开发者A:这个写起来代码量有点大,而且放单页里面代码太复杂了🥹 官方:来啦来啦~小程序页面转场动画可以使用小程序自定义路由来实现,赶紧往下 ⬇️ 看~ 我们先来看下有无自定义路由的效果对比 没有自定义路由:只能使用默认路由切换效果,从右到左推入页面使用自定义路由:支持自定义转场动画,示例下沉式路由效果[图片] 在使用默认路由的时候,只需要调用 wx.navigateTo 即可,而自定义路由,则需要先声明 1、通过 wx.router.addRouteBuilder( 命名,builder 函数 ) 来声明自定义路由 2、页面跳转新增 routeType 参数,值为上一步的命名 [图片] 接着,我们来看下声明自定义路由中 builder 函数,这个函数主要是定义两个动画 定义页面推入动画:结合 推入进度、推入状态 等参数,由开发者自定义计算得来定义页面被推出动画:结合 被推出进度、被推出状态 等参数,由开发者自定义计算得来[图片] 当打开一个新页面的过程中,就会触发自定义路由动画,此时有两个动画 新页面:打开动画旧页面:隐藏动画下图演示在页面 A 打开 页面 B,打开过程的动画效果: 新页面:页面 B 半屏打开旧页面:页面 A 页面下沉[图片] 动画除了上述讲的动画效果之外,还有一个关键的点就是动画曲线,动画曲线可以让动画效果更加丰富。 👇 下面我们可以看出来,相同的动画效果,但是使用不同的动画曲线,展示出来的页面切换效果是很不一样的。worklet 支持了常见的动画缓动函数 Easing,开发者可以根据业务需求实现页面动画切换效果。 [图片] 那么我们就来看一下页面切换动画的代码是怎么实现的~ 页面的切换动画通过 worklet 函数来实现的,worklet 函数运行在 UI 线程,使得小程序可以做到 类原生动画般的体验。 我们先来看下这里设计的新页面打开时的动画,在页面打开的过程中,开发者可以收到一个 primaryAnimation 的参数,这个参数表示当前页面从 0 到 1 打开的进度,此时,我们通过 handlePrimaryAnimation 来实现页面打开过程我们希望页面展示的动画效果,例如:改变页面高度、圆角、与顶部的偏移距离等。 这样,就实现页面展示的动画效果。 // 新页面:页面 B 半屏打开 const handlePrimaryAnimation = () => { 'worklet' // primaryAnimation 为 builder 函数的参数,表示当前页面从 0 - 1 展示动画的进度 // primaryAnimation 为 sharedValue 类型,当 primaryAnimation 变化时,这个函数就会被执行 let t = primaryAnimation.value // 非手势触发时,可以通过动画曲线 easeInToLinear 来改变动画的进度值 // worklet 支持了常见的动画缓动函数,开发者可以根据业务需求实现需要的页面切换动画效果 if (!userGestureInProgress.value) { t = wx.worklet.Easing.bezier(0.35, 0.91, 0.33, 0.97).factory()(t) } const top = 0.12 // 半屏页面距离顶部的距离比例 const selfHeight = (1 - top) * screenHeight // 半屏页面高度 const marginTop = top * screenHeight // 半屏页面距离顶部的距离 const translateY = selfHeight * (1 - t) // 页面动画过程中的纵向偏移值 // 返回 AnimatedStyle,改变页面展示 return { marginTop: `${marginTop}px`, borderRadius: '10px', height: `${selfHeight}px`, transform: `translateY(${translateY}px)`, } } 同样的,页面隐藏跟页面展示是类似的,开发收到的是 secondaryAnimation 参数,表示的是页面从 1 到 0 的关闭进度,这里我们通过 [代码]handleSecondaryAnimation 来实现页面隐藏的效果。[代码] 注意:handleSecondaryAnimation 表示下一页面推入时,当前页面的隐藏动画,在下沉式动画这个案例中,handleSecondaryAnimation 是旧页面的,而 handlePrimaryAnimation 是新页面的。 // 旧页面:页面 A 页面下沉 const handleSecondaryAnimation = () => { 'worklet' // secondaryAnimation 为 builder 函数的参数,表示下一个页面推入时,当前页面从 1 - 0 隐藏动画的进度 // secondaryAnimation 为 sharedValue 类型,当 secondaryAnimation 变化时,这个函数就会被执行 let t = secondaryAnimation.value // 非手势触发时,可以通过动画曲线 fastOutSlowIn 来改变动画的进度值 if (!userGestureInProgress.value) { t = wx.worklet.Easing.bezier(0.4, 0.0, 0.2, 1.0).factory()(t) } const top = 0.1 // 页面距离顶部的距离比例 const scaleRatio = 0.08 // 缩放比例 const translateY = screenHeight * (top - 0.5 * scaleRatio) * t // 页面动画过程中的纵向偏移值 const scale = 1 - scaleRatio * t // 缩放过程中的比例 const radius = 12 * t // 页面圆角 // 返回 AnimatedStyle,改变页面展示 return { borderRadius: `${radius}px`, transform: `translateY(${translateY}px) scale(${scale})`, } } 实现完动画切换的函数之后,我们需要包装到 builder 函数中并注册这个 builder 函数。 // 注册 builder 函数 wx.router.addRouteBuilder("HalfScreenDialog", HalfScreenDialogRouteBuilder) wx.router.addRouteBuilder("ScaleTransition", ScaleTransitionRouteBuilder) // 实现页面 B 的 builder 函数:页面打开时半屏打开 const HalfScreenDialogRouteBuilder = ({ primaryAnimation }) => { return { handlePrimaryAnimation // 页面 B 打开时的动画,上文中实现的 handlePrimaryAnimation 函数 } } // 实现页面 A 的 builder 函数:下一个页面打开时,当前页面下沉 const ScaleTransitionRouteBuilder = ({ primaryAnimation, secondaryAnimation }) => { return { handlePrimaryAnimation, // 页面 A 打开时的动画 handleSecondaryAnimation // 页面 B 隐藏时的动画,上文中实现的 handleSecondaryAnimation 函数 } } 在文章开头我们知道,声明完自定义路由之后,需要在页面跳转时指定路由类型。 到这里,通过页面跳转,返回按钮已经达到我们要的效果了。 // home.js // 首页打开页面 A, wx.navigateTo({ url: 'pageA', routeType: 'ScaleTransition', }) // page.js // 页面 A 打开页面 B wx.navigateTo({ url: 'pageB', routeType: 'HalfScreenDialog', }) 我们在体验原生页面切换时,手势也是顺滑切换的重要组成部分,在上一篇 小程序手势:让半屏弹窗更顺滑 我们已经了解手势的使用,那么我们这里给页面绑定手势,支持向右、向下拖动页面返回。 我们在页面最外层嵌套一个手势组件 horizontal-drag-gesture-handler(横向滑动时触发) 当手势向右滑动时,根据触摸位置改变页面当前的状态 触摸中:页面随手指拖动触摸结束:根据手势速度和位置判断关闭还是打开页面// .wxml // .js // 根据手势状态改变页面展示状态 // this.customRouteContext 中包含当前页面定义路由 builder 时的全部变量 handleHorizontalDrag(gestureEvent) { "worklet"; if (gestureEvent.state === GestureState.BEGIN) { // 触摸开始 const { startUserGesture } = this.customRouteContext; startUserGesture(); } else if (gestureEvent.state === GestureState.ACTIVE) { // 触摸中,实现跟随手指拖动页面效果 const delta = gestureEvent.deltaX / windowWidth; const { primaryAnimation } = this.customRouteContext; const newVal = primaryAnimation.value - delta; primaryAnimation.value = clamp(newVal, 0.0, 1.0); } else if (gestureEvent.state === GestureState.END) { // 触摸结束 const { stopUserGesture, didPop } = this.customRouteContext; ... didPop(); // 退出页面调用 stopUserGesture(); // 结束必须调用 } else if (gestureEvent.state === GestureState.CANCELLED) { // 触摸取消 } } 添加完手势之后,就可以通过手势关闭页面了~ [图片] 除了案例中实现的下沉式半屏效果,自定义路由可以根据开发者需要自行定制动画。 目前,官方提供了几个常用的路由效果供大家使用,mark 这个 代码片段 即可使用。
2023-08-03 - wx.createImage() 得到的image对象,src不能是base64的svg图片吗?
wx.createImage() 得到的image对象,src不能是base64的svg图片吗? 在标签中,image的src属性可以是base64的svg,但是在js中,image.src = 'data:image/svg+xml;utf8,xxx',触发不了image.onload函数
2023-08-01 - canvas 使用 loadFontFace 加载字体,模拟器可以,真机无效?
在使用 app.js中使用 wx.loadFontFace({ global: true, family: 'sjwbjt', source: 'url("https://my-font-library.pages.dev/jianhao.ttf")',, scopes: ["webview", "native"], success: () => { console.log("成功"); resolve(); }, fail: () => { console.log("失败"); reject(); }, }); 下列均已经检查没问题 [图片] 在canvas中 ctx.font = `${maxSize}px sjwbjt`; 模拟器下显示正常,真机ios和安卓均不生效 [图片] 可复现的代码片段 https://developers.weixin.qq.com/s/fM9jHkm87MQs 有没有官方来回答一下,sos
2024-04-25 - Skyline|小程序手势:让半屏弹窗更顺滑
在小程序页面开发中,我们经常用半屏弹窗来进来内容展示,例如:微信开放社区切换主页、加入购物车的选项页、文章留言区等等。 [图片] 常见的半屏弹窗展示逻辑是这样的: 打开弹窗:点击 “打开弹窗” 按钮展示弹窗关闭弹窗:点击“关闭按钮” or 遮罩层 关闭弹窗当我们想在半屏弹窗加一些交互动画时,可以监听节点的 touch 事件来做一些手势判断,进而处理拖拽事件。但是这种方式实现的滚动动画容易卡顿,出现延迟的情况,效果并不理想。 为了丰富小程序的交互体验,我们内置了一批手势组件,可以帮助开发者更好的实现交互动画的效果。 下图演示使用手势的半屏弹窗下拉效果与普通半屏下拉的对比。 当内部评论列表往下拉到顶部时,变为半屏的下拉,可直接下拉关闭弹窗。 [图片] 我们来看下这种操作是怎么实现的 在上面评论列表的半屏弹窗中会有一个 scroll-view 滚动组件,在 scroll-view 中会有滚动事件,当滚动到顶部时,我们希望有整个半屏的下拉事件。 所以我们需要在半屏的最外层放置一个拖动手势组件 pan-gesture-handler 由于拖动组件内部的 scroll-view 也是可以滚动的,所以这里需要进行一个手势协商的处理,就是什么条件下由哪个组件来响应手势。 当手势往下 ⬇️ 滚动时,此时判断内部 scroll-view 滚动条的位置 滚动条处于顶部:外层 pan-gesture-handler 响应滚动,此时半屏往下拖动至关闭半屏滚动条不处于顶部:内层 scroll-view 响应滚动,此时内部列表往上滚[图片] 当手势往上 ⬆️ 滚动时,此时判断半屏的位置 半屏不完全打开时:外层 pan-gesture-handler 响应滚动,此时半屏往上拖动至完全打开半屏半屏完全打开时:内层 scroll-view 响应滚动,此时内部列表往下滚[图片] 我们来看一下代码的实现,这里用到的手势组件 pan-gesture-handler(拖动时触发)和 vertical-drag-gesture-handler(纵向滑动时触发),手势组件有以下属性 on-gesture-event:手势回调事件should-response-on-move:是否响应当前手势的 move 阶段simultaneous-handlers:指定需要协商的手势是哪几个,下面演示表示 pan 和 scroll 协同触发。native-view:代理的原生节点,这里 scroll-view(scroll-y) 内有个 vertical-drag 手势,scroll-view 自身无法处理,需要被代理出来 ... 接着,我们看看在页面 js 中怎么处理手势。 在手势处理的回调中因为会改变半屏的状态值,所以这里的回调函数采用 worklet 函数,worklet 函数运行在 UI 线程,使得小程序可以做到类原生动画般的体验。 // page.js // shared 创建的变量为共享变量,可在 UI 线程和 JS 线程间同步 this.transY = wx.worklet.shared(1000) this.scrollTop = wx.worklet.shared(0) this.startPan = wx.worklet.shared(true) // shouldPanResponse 和 shouldScrollViewResponse 用于 pan 手势和 scroll-view 滚动手势的协商 shouldPanResponse() { 'worklet' return this.startPan.value }, shouldScrollViewResponse(pointerEvent) { 'worklet' // transY > 0 说明 pan 手势在移动半屏,此时 scroll-view 滚动不应生效 if (this.transY.value > 0) return false const scrollTop = this.scrollTop.value const { deltaY } = pointerEvent // deltaY > 0 是往上滚动,scrollTop <= 0 是滚动到顶部边界,此时 pan 开始生效,scroll-view 滚动不生效 const result = scrollTop <= 0 && deltaY > 0 this.startPan.value = result return !result }, // pan 手势处理 handlePan(gestureEvent) { 'worklet' if (gestureEvent.state === GestureState.ACTIVE) { const curPosition = this.transY.value const destination = Math.max(0, curPosition + gestureEvent.deltaY) // 改变半屏的位置 this.transY.value = destination } // 其他手势状态的处理,如滚动结束时计算半屏处于打开还是关闭的状态 } 目前,同程旅行 已经上线了手势结合半屏的效果 体验路径:酒店查询 - 选择酒店 - 选择入住人 - 新增入住人 [图片] 普通半屏结合手势代码片段:https://developers.weixin.qq.com/s/lx0RH1mD7rGj 手势除了在普通半屏的应用之外,也可以实现分段式半屏。下面演示的分段式半屏比普通半屏的判断条件更多一些。 判断条件同普通半屏类似,根据手势方向 和 分段式半屏当前的位置来判断是响应分段式半屏还是内部列表,响应分段式半屏是改变到哪一个位置。 [图片] 这里与普通半屏不同的是我们还改变了地图的缩放级别(scale) 因为 worklet 函数是在 UI 线程运行的,当要改变 data 值时,需要通过 wx.worklet.runOnJS 调回 JS 线程。 // page.js // 设置 map scale // 运行在 JS 线程 setMapScale(scale) { this.setData({ scale }) }, // worklet 函数,运行在 UI 线程 scrollTo(toValue) { 'worklet' let scale = 18 if (toValue > screenHeight / 2) { scale = 16 } // 从 UI 线程调回 JS 线程 wx.worklet.runOnJS(this.setMapScale.bind(this))(scale) this.transY.value = timing(toValue, { duration: 200 }) }, // 处理拖动半屏的手势 handlePan(gestureEvent) { 'worklet' // 滚动半屏的位置 if (gestureEvent.state === GestureState.ACTIVE) { // deltaY < 0,往上滑动 this.upward.value = gestureEvent.deltaY < 0 // 当前半屏位置 const curPosition = this.transY.value // 只能在 [statusBarHeight, screenHeight] 之间移动 const destination = clamp(curPosition + gestureEvent.deltaY, statusBarHeight, screenHeight) if (curPosition === destination) return // 改变 transY,来改变半屏的位置 this.transY.value = destination } if (gestureEvent.state === GestureState.END || gestureEvent.state === GestureState.CANCELLED) { if (this.transY.value <= screenHeight / 2) { // 在上面的位置 if (this.upward.value) { this.scrollTo(statusBarHeight) } else { this.scrollTo(screenHeight / 2) } } else if (this.transY.value > screenHeight / 2 && this.transY.value <= this.initTransY.value) { // 在中间位置的时候 if (this.upward.value) { this.scrollTo(screenHeight / 2) } else { this.scrollTo(this.initTransY.value) } } else { // 在最下面的位置 this.scrollTo(this.initTransY.value) } } }, 分段式页面代码片段:https://developers.weixin.qq.com/s/fw0U31mI7bGf 半屏的交互除了在页面内实现,也能跨页面实现,如常见的下沉式半屏交互。其中,半屏效果与上述实现类似,而前一页面的下沉实现需要结合自定义路由 后面的文章中我们会介绍自定义路由结合手势怎么去实现下沉式半屏效果,不仅如此,还有很多类原生的页面切换效果都能通过自定义路由实现 [图片]
2023-08-03 - 手势组件pan-gesture-handler等在canvas上消失
https://developers.weixin.qq.com/miniprogram/dev/framework/runtime/skyline/changelog.html <view class="bingo-packer"> <pan-gesture-handler worklet:ongesture="onGesture"> <view class="bingo-packer__canvas_wrap"> <canvas type="2d" id="{{cid}}" class="bingo-packer__canvas" disable-scroll="{{true}}" style="z-index: 0;" /> </view> </pan-gesture-handler> </view> onGesture(e) { "worlet" console.log(e); }
2024-01-14 - canvas 组件在iOS 设备上绘制速度太慢, 原因是什么?
canvas 组件在iOS 设备上绘制速度太慢, 原因是什么? canvas 2d 在苹果真机 大屏手机上 页面上绘制一个二维码 ,需要超过10秒才能万完成绘制. 请问一下原因是什么
2023-07-14 - canvas重绘卡顿
场景是在拍摄的现场图片上面进行画线标记,在每一根线上写上长度(例如1000mm)。 用到了 touchstart,touchmove,touchend 在移动过程中要不停的清除画布,然后重新绘制所有的线条。在微信开发者工具中画线流畅,在手机上就会很卡,而且线条越多绘制过程会越卡。 [图片] [图片] [图片] [图片]
2022-12-26 - 【已解决】windowHeight(可使用窗口高度)是如何定义的?
问题: https://developers.weixin.qq.com/miniprogram/dev/api/base/system/wx.getWindowInfo.html wx.getWindowInfo()这个API返回的对象中的windowHeight具体是如何定义的? 官方文档似乎没有找到明确的定义。 是不是可以理解为:屏幕高度减去状态栏高度再减去标题栏高度? [图片] 如果不是的话请问准确的定义是什么? ------------------------------------------------- windowHeight定义: 此字段的值取决于navigationBar和tabBar是否自定义。 仅自定义navigationBar,windowHeight = screenHeight - tabBarHeight 仅自定义tabBar,windowHeight = screenHeight - navigationBarHeight 同时自定义navigationBar和tabBar,windowHeight = screenHeight
2022-04-28 - 我们和纺织公司帮助微信出海占领市场,但是现在微信反而给我们拖后腿,想和大家探讨一下?
我们的小程序是做SAAS服务的,为中国广大纺织厂家服务。 国内的纺织业企业至少有几十万家公司。 这些纺织厂家有做内贸,也有做外贸的,他们的出口业务范围遍布全球。 我们就是为这些厂家提供软件服务,帮忙注册他们自己的小程序抬头,再帮他们部署小程序。 使用他们的小程序用户,可以在我们的小程序集中查看当前用户看过的厂家和看过的产品,以及哪些产品下过单和下单信息(去除价格)。 如果是纺织外贸企业,我们的软件还会帮助他们把产品内容翻译为客户手机微信的语言。 比如客户的微信用的是法语,那么我们就帮助翻译成法文。 我们和纺织业出口企业相当于帮助腾讯把微信推广到全球海外纺织以及服装行业从业人员,这个人数是巨大的。 这些用户还可以在线下展会扫纺织厂家的二维码,浏览纺织厂家的产品。 这个举动也可能会顺便带动展会其他国家的纺织厂家也注册微信小程序。 但是现在微信不允许用户使用手机号码登陆我们的小程序,即便我们的用户协议中已经说清楚,手机号码是当用户在纺织厂下单后,厂家需通过手机号码联系用户。 了解下来纺织行业的厂家和用户都觉得不通过手机号联系不方便。 因为从事劳动密集型行业的他们不习惯使用邮箱和其他账号,而且纺织业货值金额巨大,厂家仅仅凭借无法查找具体人员,核实对方企业信息的账号密码,因此不敢备货。 这个问题该怎么解决? 也希望腾讯官方了解一下我们这个情况,毕竟老外用的人数越多,微信的海外市场占有率也会上来 中国的纺织业吸纳了众多初中文化的工人,很多人学历水平不高,老外可能使用邮箱,但是国内的纺织业从业人员并不会邮箱或者账号密码之类的,他们最多能报个手机号码已经是很好了 ----------- 大家可能会有疑问,为什么要在我们自己的小程序提供集中式的查看。 因为纺织行业企业非常多,不管是生产企业还是外贸企业。 有的用户作为采购面料的服装企业,在行业展会,往往会面对上万家纺织企业,对,是上万家。 让用户去注册上万家企业的小程序,用上万个账号密码登陆,非常不现实的。 而且当他扫过二维码,注册了,下单了,只凭借一个abc123账号要准备几万元甚至几十万元的货物,哪个老板敢冒这个风险。 纺织行业有的面料很贵,一定要联系到用户,确认他所在的公司信息,否则厂家不会搭理的。 目前这样就无法帮助内贸纺织厂家销售,也无法帮助外贸企业出口。 国内纺织业帮助微信出海也无从谈起。 我们,纺织行业,腾讯,是个三输的局面。 我们现在就很困惑,该怎么解决这个问题。
2022-08-19 - 这些 Canvas 小技巧,保证你新年用得上
来自「微信开发者」公众号,作者为微信小程序技术研发工程师binnie。 本文主要介绍了3个隐藏的 Canvas 小技巧: - 绘制并生成图片 - Video 绘制 Canvas / webgl - 视频解码并绘制到 webgl - 录制并导出 webgl 视频 一键加滤镜 快速合成音视频 轻松挑选视频封面 …… Canvas 能够做这些? 作为资深的开发者,相信大家对 Canvas 都不陌生。这项能力在绘制图形方面发挥着极大的作用,高效支持图片编辑、数据可视化等应用场景。但是只局限于一般能力应用,那格局就小了。 Canvas 的应用场景非常丰富!赶紧往下看看这些隐藏的 Canvas 小技巧,保证你新年用得上!还有手把手教程以及文末彩蛋哟。 -- • 绘制并生成图片 • -- [图片] 示例:新年模板长按保存祝福 适用场景:图片分享海报 相关 API:RenderingContext/Canvas/wx.canvasToTempFilePath Step 1: 创建实例获取对象 创建 Canvas 实例,获取 CanvasRenderingContext2D 对象(Canvas 绘图上下文)来绘制形状、文本、图像等。 const query = wx.createSelectorQuery() let canvas = null query.select('#myCanvas') .fields({ node: true, size: true }) .exec((res) => { // 通过 wx.createSelectorQuery 获取到 canvas 实例 canvas = res[0].node // 通过 canvas.getContext('2d') 获取 CanvasRenderingContext2D 对象 const ctx = canvas.getContext('2d') }) Step 2: 设置宽高调整图片 获取 Canvas 绘图上下文后,将 Canvas 的宽高设置为节点宽高 * 设备像素比,绘制出来的图片更清晰 // 获取设备像素比 const dpr = wx.getSystemInfoSync().pixelRatio // 将 canvas 宽高设置为 canvas.width = res[0].width * dpr canvas.height = res[0].height * dpr Step 3: 绘制内容 使用 CanvasRenderingContext2D 绘制,根据业务需要在画布中绘制头像、文字、背景等 // 矩形 ctx.fillStyle = '#FFFFFF' ctx.fillRect(0, 0, canvas.width , canvas.height ) // 图片 var image = canvas.createImage() himage.src = 'https://example.com/example.jpg' headImage.onload = (res) => { ctx.drawImage(himage 0, 0, 32, 32; } // 文本 ctx.font = "18px SimHei"; ctx.textAlgin = "left" ctx.fillStyle = "#07c160"; ctx.fillText("这是我的名字", 0, 0); Step 4: 生成并保存本地 使用 wx.canvasToTempFilePath 将画布生成图片,wx.saveImageToPhotosAlbum 将图片保存到本地。 wx.canvasToTempFilePath({ canvas: canvas, // canvas 实例 success(res) { // canvas 生成图片成功 wx.saveImageToPhotosAlbum({ filePath: res.tempFilePath, success(res) { // 保存成功 } }) } }) -- • Video 绘制 Canvas / webgl • -- [图片] 示例:视频文件绘制 Canvas 适用场景:制作 Video 滤镜、挑选 Video 封面等 相关 API:RenderingContext/Canvas Step 1: 获取实例 通过 wx.createSelectorQuery 获取 VideoContext 实例 let video = null wx.createSelectorQuery().select('#video').context(res => { // 通过 wx.createSelectorQuery 获取 VideoContext 实例 video = res.context; }) Step 2: 绘制内容 获取 VideoContext 实例后,将 VideoContext 传递给 Canvas 进行绘制。开发者根据业务需求选择绘制类型: Canvas 2d 写法:canvas.drawImage(video, ...)webgl 写法:gl.texImage2D(..., video) wx.createSelectorQuery().selectAll('#myCanvas,#webglCanvas').node(res => { const ctx = res[0].node.getContext('2d') const gl = res[1].node.getContext('webgl') setInterval(() => { // canvas 2d // 将 video 纹理对象传入 drawImage 进行绘制 ctx1.drawImage(video, 0, 0, w * dpr, h * dpr); // 添加一个蒙层 ctx1.fillStyle = 'rgba(0, 0, 0, 0.3)' ctx1.fillRect(0, 0, w * dpr, h * dpr); // webgl const render = createRenderer(res[1].node, w, h) render(new Uint8Array(ctx1.getImageData(0, 0, w * dpr, h * dpr).data), w * dpr, h * dpr) }, 1000 / 24) }).exec() function createRenderer(canvas, width, height) { const gl = canvas.getContext("webgl") ... return (arrayBuffer, width, height) => { ... // 指定二维纹理图像 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, arrayBuffer) gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0) } } -- • 视频解码并绘制到 webgl • -- [图片] 示例:视频一键解码并绘制到 webgl 适用场景:添加特效、贴图等视频编辑场景 相关 API:wx.createVideoDecoder/VideoDecoder/RenderingContext/Canvas.requestAnimationFrame/wx.createMediaAudioPlayer/MediaAudioPlayer Step 1: 创建视频解码器进行解码 1. 调用 createVideoDecoder 对视频进行解码 2. 使用 videodecoder.start 启动解码,视频源文件不限制本地或远程路径 3. 通过 videodecoder.on('start', res => {}) 监听解码,通过 videodecoder.getFrameData() 获取到解码数据 // 获取视频解码器 getVideoDecoder(source, abortAudio) { return new Promise((resolve, reject) => { // 创建视频解码器 videodecoder = wx.createVideoDecoder() // 开始解码 videodecoder.start({ abortAudio: abortAudio, source: source, // 视频源文件,支持本地路径&远程路径 mode: 0 // 按pts解码,保证音画同步 }) // 监听解码 开始 videodecoder.on('start', res => { console.log('videodecoder start', res) // 状态初始化 isStop = false resolve(videodecoder) }) // 监听解码 结束 videodecoder.on('ended', res => { // 状态设置为结束,停止画面录制器 isStop = true }) }) }, Step 2: 解码数据绘制到 webgl 1. 通过 gl.texImage2D(..., image) 将解码数据绘制到 webgl 2. 使用 webgl.requestAnimationFrame 继续绘制,效果更加流畅 // 将解码数据绘制到 webgl 中 const query = wx.createSelectorQuery() query.select('#webglCanvas').node().exec((res) => { const webgl = res[0].node const requestAnimationFrame = webgl.requestAnimationFrame; // 初始化webgl let render = null if (!render) { render = createRenderer(webgl, 600, 400) } /** * 绘制视频帧到 canvas */ let i = 1 let loop = () => { // 解码结束,停止循环 if (isStop) { return } // 获取解码数据,绘制到 webgl 中 const imageData = videodecoder.getFrameData() if (imageData) { // render 的高宽需要设置为图片的宽高才可以绘制出来 render(new Uint8Array(imageData.data), imageData.width, imageData.height) } // 继续绘制 console.log('绘制帧数:', i++) requestAnimationFrame(loop) } // 启动录制循环 requestAnimationFrame(loop) }) Step 3: 添加音频播放器同步播放音频 完成 Step2 后,webgl 只有视频播放,缺少音频。因此使用 wx.createMediaAudioPlayer(),支持 addAudioSource 传入 videodecoder,保证视频帧渲染音画同步 /** * 创建媒体音频播放器 */ let mediaAudioPlayer = null let addAudio = () => { if (mediaAudioPlayer) return mediaAudioPlayer = wx.createMediaAudioPlayer() mediaAudioPlayer.start().then(() => { // 添加播放器音频来源 mediaAudioPlayer.addAudioSource(videodecoder).then(res => { console.log('add mediaAudioPlayer: ',) }) }) } // render 绘制视频同时添加音频 render(new Uint8Array(imageData.data), imageData.width, imageData.height) addAudio() -- • 录制并导出 webgl 视频 • -- [图片] 示例:录制并一键导出 webgl 视频 适用场景:将动画、编辑过的视频导出视频文件保存 相关 API:wx.createMediaRecorder/MediaRecorder/wx.createMediaContainer/MediaContainer/MediaTrack Step 1: 创建 webgl 画面录制器进行录制 通过 createMediaRecorder 创建页面录制器,并且绑定 webgl(建议离屏状态,效果更好)进行录制 /** * 获取画面录制器 */ getRecorder() { let canvas = this.getMainCanvasNode() let recorder = wx.createMediaRecorder(canvas, { fps: choosedVideoInfo.fps, // 实际视频的 fps videoBitsPerSecond: choosedVideoInfo.bitrate, // 实际视频的 bitrate gop: 12 }) // 监听录制事件 recorder.on("timeupdate", (res) => { console.log('recorder 录制中,当前时间:', res.currentTime) }) recorder.on("stop", (res) => { console.log('recorder停止') this.saveMedia(res.tempFilePath) }) // 开始录制 recorder.start() this.recorder = recorder return recorder }, // 初始化 画面录制器 并进行录制 await this.initRenderer() this.getDecoder().then((decoder) => { let recorder = this.getRecorder() var self = this function loop() { if (self.stopped) { return } let frameData = decoder.getFrameData() if (!frameData) { console.log('没取到帧') setTimeout(() => { loop() }, 1000/60) } else { self.renderFrame(frameData) recorder.requestFrame(() => { console.log('录制帧数:', i++) loop() }) } } loop() }) Step 2: 添加音频合成音视频 1. 通过 createMediaContainer 创建音视频处理容器来合成音视频 2. 通过 MediaContainer.extractDataSource 将视频源分离出视频轨道和音频轨道,将需要的轨道通过 MediaContainer.addTrack 添加到容器中 3. 通过 MediaContainer.export 导出即可获得合成后的视频文件 /** * 将视频和音频合到一起并保存到本地 * @param {*} videoTempFilePath */ saveMedia(videoTempFilePath) { const self = this let choosedFile = this.choosedFile const MediaContainer = wx.createMediaContainer() // webgl的取视频 MediaContainer.extractDataSource({ source: videoTempFilePath, success(res) { MediaContainer.addTrack(res.tracks[0]) // 源视频取音频 MediaContainer.extractDataSource({ source: choosedFile, success(res) { // 拿到音频轨道并加入到容器 res.tracks[0].kind == 'audio' && MediaContainer.addTrack(res.tracks[0]) res.tracks[1].kind == 'audio' && MediaContainer.addTrack(res.tracks[1]) // 合成视频并导出视频文件 MediaContainer.export({ success(res) { // 保存视频到本地 wx.saveVideoToPhotosAlbum({ filePath: res.tempFilePath, success() { wx.showToast({ title: '导出成功', icon: 'success', duration: 2000 }) self.destroy() } }) } }) } }) } }) }, -- •高效图像处理彩蛋 • -- 学会以上这些 Canvas 小技巧,还担心新年的美图美照美视频处理不过来?赶紧码下这个 Canvas 代码包,保证你就是家里最闪耀的靓女靓仔。 预祝大家新的一年 Canvas 在手,红包一直有!
2022-03-24 - sub mch id与sub_appid不匹配?
[图片]sub mch id与sub_appid不匹配问题 这边三方技术说没问题 给出的结果是腾讯最近不允许第三方支付平台接口 想核实一下到底什么情况 还是有最新出的什么规定导致的
2023-07-15 - 调用新的wx.chooseMedia选择了Heic格式的图片,无语预览,也无法获取图片信息?
wx.chooseMedia({}).then(res => { console.log(res) this.setData({ imgUrl: res.tempFiles[0].tempFilePath }) wx.getImageInfo({ src: res.tempFiles[0].tempFilePath, }).then(res => { console.log(res) }).catch(err => { console.log(err) }) }) 选择了heic格式的图片,图片显示不出来,使用getImageInfo也无法获取图片信息.报错为: {errMsg: "getImageInfo:fail invalid"} 这个情况应该怎么处理? 用户上传了两张heic格式的图片无法显示.
2023-12-26 - 关于wx.chooseMedia,隐私条款已经授权,本地和真机调试没问题,发布到体验版本有问题?
[图片] 代码亲测,已经授权走的 下边这个方法,然后调用直接报错{errmsg:'chooseMeida':'ok','tem...':},此代码在真机模拟。本地运行完全OK 但就是上传到体验版本不行,求大神们讲解!! {"errMsg": "chooseMedia:fail api scope is not declared in the privacy agreement", "errno": 112}
2024-02-29 - skyline之下,wxs还能响应事件吗?
在skyline渲染的时候,wxs还能响应事件吗??就是这个链接里面的内容https://developers.weixin.qq.com/miniprogram/dev/framework/view/interactive-animation.html 在skyline下,若不能,js里面的方法还能触发wxs里面的方法,并在wxs中更新页面元素的style。有没有平替方案
2023-11-15 - "renderer" 为 "skyline",引用weui库报错,怎么可以全局配置?目前一个个加
[ miniprogram_npm/weui-miniprogram/form-page/form-page.json 文件内容错误] miniprogram_npm/weui-miniprogram/form-page/form-page.json: 根据页面或 app.json 的配置,miniprogram_npm/weui-miniprogram/form-page/form-page.json 页面 "renderer" 为 "skyline",需在页面配置中添加 "disableScroll": true(env: Windows,mp,1.05.2204264; lib: 3.3.4)
2024-03-05 - Webview、Skyline 混用切换耗时吗?
在学习 Skyline 的过程中,许多开发者会有一个疑问:是否可以将小程序的部分页面迁移到 Skyline? 对 Skyline 感兴趣但还没有完全决定是否要使用的开发者来说,可能只想先尝试一下 Skyline 的功能。 实际上,Skyline 支持最小粒度的页面配置,意味着我们可以为某个页面单独开启 Skyline,而不必将整个小程序迁移到 Skyline 上。 开发者可以更加灵活地使用 Skyline,并逐步将小程序迁移到 Skyline 上,从而获得更好的性能和用户体验。 我们知道 Webview 和 Skyline 是两个渲染引擎,对于 Webview 和 Skyline 混用,大家又有新的疑问:当进行页面切换的时,混用是否会增加耗时? 这里需要分三种情况: 1、Skyline -> Webview:这种情况取决于 app.json 里配置的全局 renderer,即小程序设置的默认渲染引擎 如果全局 renderer 是 Skyline,那么 Webview 不会被预加载,此时 Skyline 跳转 Webview 耗时会增加,开发者需要手动调用 wx.preloadWebview 做预加载。如果全局 renderer 是 Webview,由于 Webview 默认会预加载,所以 Skyline -> Webview 和 Webview -> Webview 耗时一样,不会增加耗时。2、Webview -> Skyline:Skyline 默认都不会被预加载,开发者需要手动调用 wx.preloadSkylineView 做预加载。 3、Skyline -> Skyline:速度变快,因为多个页面复用同一个 Skyline 实例。 根据上述三种情况的分析,为了保证混用渲染引擎的页面切换耗时最短,我们需要在以下时机进行预加载。 wx.preloadWebview 当 Skyline 页面跳转到 Webview 页面时并且全局 renderer 是 Skyline 由于 Skyline 不影响渲染线程,所以预加载 Webview 的时机只需要在主要逻辑完成后即可 // Skyline page.js Page({ onShow() { // 等待执行完主要逻辑后进行预加载 wx.preloadWebview() } }) wx.preloadSkylineView 当 Webview 页面跳转 Skyline 页面时,因为 Skyline 默认不预加载,所以我们需要手动预加载。 建议大家在 Skyline 页面的 onShow 生命周期里延迟一段时间后调用,这样可以保证在 Skyline 页面被返回时也能够重新预加载。 注意:预加载会影响当前页面的渲染,建议异步延迟去执行预加载操作 // Webview page.js Page({ onShow() { // 延迟 200ms 预加载 Skyline // 建议这个延迟时机在页面渲染完成之后 setTimeout(() => { wx.preloadSkylineView() }, 200) } }) 做好预加载是提高 Webview 和 Skyline 混用体验的有效方式,需要根据实际情况进行调整和优化,以达到最佳的预加载效果。
2024-03-07 - masonry状态下瀑布流里面使用自定义组件失效?
[图片][图片]
2023-10-12 - 请问有大神知道小程序报错skylineWindow 29 is not exist是怎么回事吗?
开发者工具版本:RC 1.06.2402021 报错信息及调试库版本信息如下,期待被大神看到,非常感谢。谢谢。 [图片][图片]
2024-04-14 - 从page-container跳转下一页面,再返回,page-container页返回按钮无效?
page-container组件、开发工具最新版、基础库2.22; 问题描述:从首页跳转详情页,在详情页打开page-container并跳转编辑页(编辑页在分包中),再从编辑页返回详情页后,详情页的返回按钮失效,无法返回首页。只有再次打开page-container并关闭,返回按钮才有效。 代码片段: https://developers.weixin.qq.com/s/eRkZy2mh7Dxc
2022-02-13 - 小程序组件的函数属性及事件触发
开发小程序组件库 TDesign 有感 微信小程序,从基础库 [代码]2.0.9[代码] 开始,自定义组件的 [代码]type: Object[代码] 属性(properties)支持函数类型的值了,但仍不支持函数类型的属性,即: [代码]// dialog.js Component({ properties: { confirmBtn: { type: Object, // ok }, cancelBtn: { type: Function // wrong } }, observer: { confirmBtn(obj) { console.log(obj.bindgetuserinfo) // function } } }) [代码] 这种能力,在实现 Dialog 组件的时候,非常有用。这样在 Dialog 组件的 [代码]cancel[代码] 和 [代码]confirm[代码] 按钮可以方便地支持 Button 的各种开放能力。 于是,就会想当然地这样实现: [代码]<view class="t-dialog"> <!-- ... --> <button class="cancel-btn" size="{{cancelBtn.size}}" type="{{cancelBtn.type}}" plain="{{cancelBtn.plain}}" disabled="{{cancelBtn.disabled}}" open-type="{{cancelBtn.openType}}" bindgetuserinfo="{{cancelBtn.bindgetuserinfo}}" > 取消 </button> <button class="confirm-btn" size="{{confirmBtn.size}}" type="{{confirmBtn.type}}" plain="{{confirmBtn.plain}}" disabled="{{confirmBtn.disabled}}" open-type="{{confirmBtn.openType}}" bindgetuserinfo="{{confirmBtn.bindgetuserinfo}}" > 确认 </button> </view> [代码] 这样就会出现几个问题: 属性透传写法太冗余 事件不会触发 按钮内容没法传入 属性透传 Dialog 组件存在两个按钮,所以两个按钮都需要透传 button 属性,直观的想法就是采用 template 来处理: [代码]<!-- button.wxml --> <template name="button"> <button class="{{class}}" size="{{size}}" type="{{type}}" plain="{{plain}}" disabled="{{disabled}}" open-type="{{openType}}" bindgetuserinfo="{{bindgetuserinfo}}" > 确认 </button> </template> [代码] 于是 Dialog 的代码就可以省略成这样: [代码]<import src="./button.wxml" /> <view class="t-dialog"> <!-- ... --> <template is="button" data={{...cancelBtn, class: 'cancel-btn'}}> <template is="button" data={{...confirmBtn, class: 'confirm-btn'}}> </view> [代码] 这里确实挺奇怪的,可以直接传入了一个解构后的值。 这里可以直接合并对象 事件不会触发 一开始以为是 template 的值传递过程,不支持 function 类型的值,因此丢失了。 比如在 template 里面使用 wxs 打印类型,居然是空的。 后来经过各种测试,最后在官网文档找到答案:小程序框架/事件系统 在小程序的事件绑定,只需要传入的是字符串: [代码]<view bindtap="handletap">Tap me!</view> [代码] 也可以是一个数据绑定: [代码]<view bindtap="{{ handlerName }}">Tap me!</view> [代码] 但,这个数据的返回值类型应该是 string 而不是 function。 通过这点,恍然大悟,想起了小程序的双线程模型: [图片] 为了减轻线程之间的传输负担,是不需要将 function 传到渲染层的,只需要给一个函数名,然后在逻辑层执行对应的函数即可。 因此没有办法在 wxml 里面执行对象属性的函数,需要找一个代理函数(Proxy function)处理。 为了区分对应的按钮,因此 template 做了小改动,增加了一个 [代码]data-token[代码] 的属性: [代码]<template name="button"> <button data-token="{{token}}" bindtap="onTplButtonTap"> </template> [代码] 对应的 Dialog 的 wxml 的改动是这样的: [代码]<import src="button.wxml" /> <view class="t-dialog"> <!-- ... --> <template is="button" data={{...cancelBtn, token: 'cancel', class: 'cancel-btn'}}> <template is="button" data={{...confirmBtn, token: 'confirm', class: 'confirm-btn'}}> </view> [代码] 对应的 JS 是这样的: [代码]Component({ methods: { onTplButtonTap(e) { const { token } = e.target.dataset // cancel or confirm const evtType = e.type // 对应的事件名,如 getuserinfo/getphonenumber 等 const evtName = `bind${evtType}` const targetBtn = this.data[`${type}Btn`] if (typeof targetBtn[evtName] == 'function') { targetBtn[evtName](e.detail) } } } }) [代码] 这样就能完美透传并触发各种 button 事件了。 按钮内容传入 其事这个倒是个小问题,因为 TDesign 组件在规划的时候,就已经充分地考虑了多框架之间的差异。为了弥补框架之间的差异,都可以通过 content 的属性来传入插槽的内容,起初我还不理解,直到遇到了这个问题。 以前总觉得,可以通过 slot 的方式传入,又支持一个 content 有点多此一举。直到我遇到了需要透传 button 属性的 dialog 组件。 总结 小程序的黑盒子运行时,在遇到问题的时候真的很容易陷入盲调的困境,此时应该去看看官方文档的资料,或者网上搜一下是否其他人也遇到类似的问题,这样才可能破局。 毕竟只有他们才知道代码是怎么跑的。
2022-12-11 - 解锁小程序中使用SVG新姿势
SVG 的优势 清晰度: 可以进行放大,而不失真 更小的文件体积 可扩展性,可以动态颜色 动效 可以添加动效 在小程序中使用 目前小程序 的image标签已经支持了 svg 的显示 [代码] <image src="./xx.svg"/> [代码] 如何动态的改变 svg 属性呢? 大体思路:把svg转成 base64 然后通过 image标签 src设置图片,再动态赋值svg颜色 把svg转成base64 如下一个svg 代码文件 [代码]<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="24 24 48 48"><animateTransform attributeName="transform" type="rotate" repeatCount="indefinite" from="0" to="360" dur="1400ms"></animateTransform><circle cx="48" cy="48" r="20" fill="none" stroke="#eeeeee" stroke-width="2" transform="translate\(0,0\)"><animate attributeName="stroke-dasharray" values="1px, 200px;100px, 200px;100px, 200px" dur="1400ms" repeatCount="indefinite"></animate><animate attributeName="stroke-dashoffset" values="0px;-15px;-125px" dur="1400ms" repeatCount="indefinite"></animate></circle></svg> [代码] 转成base64,其实就是 对这个svg进行 encodeURIComponent 得到 如下代码 [代码]%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22100%25%22%20height%3D%22100%25%22%20viewBox%3D%2224%2024%2048%2048%22%3E%3CanimateTransform%20attributeName%3D%22transform%22%20type%3D%22rotate%22%20repeatCount%3D%22indefinite%22%20from%3D%220%22%20to%3D%22360%22%20dur%3D%221400ms%22%3E%3C%2FanimateTransform%3E%3Ccircle%20cx%3D%2248%22%20cy%3D%2248%22%20r%3D%2220%22%20fill%3D%22none%22%20stroke%3D%22%23eeeeee%22%20stroke-width%3D%222%22%20transform%3D%22translate%5C(0%2C0%5C)%22%3E%3Canimate%20attributeName%3D%22stroke-dasharray%22%20values%3D%221px%2C%20200px%3B100px%2C%20200px%3B100px%2C%20200px%22%20dur%3D%221400ms%22%20repeatCount%3D%22indefinite%22%3E%3C%2Fanimate%3E%3Canimate%20attributeName%3D%22stroke-dashoffset%22%20values%3D%220px%3B-15px%3B-125px%22%20dur%3D%221400ms%22%20repeatCount%3D%22indefinite%22%3E%3C%2Fanimate%3E%3C%2Fcircle%3E%3C%2Fsvg%3E [代码] 拼接base64 [代码] data:image/svg+xml;charset=utf-8,encodeURIComponent后的代码 [代码] 在对应svg属性上动态设置颜色,比如这里用到的是填充颜色 在js文件 data中定义 color 状态 在wxml中动态渲染 [代码] <image src="data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22100%25%22%20height%3D%22100%25%22%20viewBox%3D%2224%2024%2048%2048%22%3E%3CanimateTransform%20attributeName%3D%22transform%22%20type%3D%22rotate%22%20repeatCount%3D%22indefinite%22%20from%3D%220%22%20to%3D%22360%22%20dur%3D%221400ms%22%3E%3C%2FanimateTransform%3E%3Ccircle%20cx%3D%2248%22%20cy%3D%2248%22%20r%3D%2220%22%20fill%3D%22none%22%20stroke%3D%22%23{{color}}%22%20stroke-width%3D%222%22%20transform%3D%22translate%5C(0%2C0%5C)%22%3E%3Canimate%20attributeName%3D%22stroke-dasharray%22%20values%3D%221px%2C%20200px%3B100px%2C%20200px%3B100px%2C%20200px%22%20dur%3D%221400ms%22%20repeatCount%3D%22indefinite%22%3E%3C%2Fanimate%3E%3Canimate%20attributeName%3D%22stroke-dashoffset%22%20values%3D%220px%3B-15px%3B-125px%22%20dur%3D%221400ms%22%20repeatCount%3D%22indefinite%22%3E%3C%2Fanimate%3E%3C%2Fcircle%3E%3C%2Fsvg%3E" /> [代码] [代码]注意:这里的颜色 由于是已经被编码了,所以# 已经被转义了 %23, 直接写颜色数字即可[代码] 当然你也可以 去掉%23 自己实现一个内部方法 [代码] if (color && color.startsWith('#')) { return `%23${color.slice(1)}`; } [代码] 这样其实就实现了 svg的动态渲染,可是这种写法,写在wxml中 不是特别的优雅,那么如何重构下让我们的代码看起来更优雅呢? 把 svg 单独存放 支持动态返回 动态赋值 image src 属性 svg 动态函数 loading.svg.js 文件 [代码]export const loadingSvg = (color='#ddd') =>{ const svgXml = `<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="24 24 48 48"><animateTransform attributeName="transform" type="rotate" repeatCount="indefinite" from="0" to="360" dur="1400ms"></animateTransform><circle cx="48" cy="48" r="20" fill="none" stroke="${color}" stroke-width="2" transform="translate\(0,0\)"><animate attributeName="stroke-dasharray" values="1px, 200px;100px, 200px;100px, 200px" dur="1400ms" repeatCount="indefinite"></animate><animate attributeName="stroke-dashoffset" values="0px;-15px;-125px" dur="1400ms" repeatCount="indefinite"></animate></circle></svg>` return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgXml)}` } [代码] 逻辑层引入,setData [代码] onLoad(){ const { loadingSvg } = require('./loading.svg.js') const svgImg = loadingSvg('#eee') this.setData({svgImg}) }, [代码] 渲染层使用 [代码] <image src="{{svgImg}}"/> [代码] github 使用案例 demoFormpSvg
2022-04-30 - 小程序什么时候支持svg交互?
现在用image引入的svg只支持显示,交互事件全部被屏蔽了,导致需要交互的动画都只能用canvas实现,用canvas的代价太大了,使用起来又非常不方便,如果支持事件能完美解决小程序大部分动画,用户体验直线上升啊@官方
2022-03-03 - 关于工作协同能力上的小程序支持的建议 ?
问题是围绕:个人的小程序卡片没有 shareTicket,和authPrivateMessage不支持云调用解密敏感信息。 需要实现的场景是工作协同,比如一项工作,A做了一部分,现在提交给负责人确认,需要用到卡片转发(*转到个人不是群),转发现支持两种方式,一是动态消息,一是私密消息。 一:动态消息, 整个场景必面用到activityID 来关联业务, 动态消息,转给个人不支持shareTicket ,没法拿到activityID 。所以走不通。但转到群里是可以的,不知道为什么个人不支持shareTicket ? 二:私密消息 查了下文档也测试了,转给个人是可以得到shareTicket ,但是这里也有个大问题,创作activityID 是支持云调用的,解密时不支持, 不像getShareInfo 可以用CloudID 来换取敏感数据,这样的话,不得不去维护每个用户的session_key ,从而来解密,我们没有必要去维护每个登录用户的Session_key。 总结:所以协同的场景,方式一如果个人支持ShareTicket 就好了, 方式二虽然是支持,但是这里又有新的问题,就是解密不支持云调用,类getShareInfo可以传入cloudID...... , 需要wx.authPrivateMessage返回cloudID , 这个就流畅了,感觉这块官方欠考虑,云调用这块没有支持到位,看现在文档给人以假象还以为是支持的,不知道是不是遗漏这块。 所以强烈建议wx.authPrivateMessage能返回cloudID。
2022-05-23 - 微信物流服务接入指引
微信物流服务是针对小程序内物流场景的系列解决方案,涵盖快递和同城配送,涉及到发货、查件、退货等环节。 商家只需引入组件,即可获得优质、稳定的物流服务,提高小程序经营效率和用户体验。
2022-03-29 - 微信服务平台简介
[视频] 电商直播、社区拼团、外卖点餐等越来越多的小程序玩法,引爆小程序的商业潜力,小程序经济时代真正来临。 商家线上数字化需求急增,各行业服务商业务量喷发,承接数字化市场巨量需求,微信小程序生态加速推进,数以万计的服务商,覆盖百万小程序商家,成为繁荣生态不可或缺的力量和贡献者。 迎来机遇的同时,也面临挑战: 许多商家想涉足小程序,但不会开发;有了小程序,又不会运营;面对微信海量用户,却对推广毫无头绪。 开发者也有痛点希望得到解决:人脸核身、视频/音乐、内容安全等相对高门槛的能力,中小开发者不易独立完成,急需靠谱、专业的成熟方案,以便低门槛快速使用。 为此,微信服务平台上线权威、全面、专业的认证服务,提供覆盖商家、开发者全流程各种需求的多品类优质服务。 通过平台,服务商能为商家解决小程序开发、运营、推广“三大难”;还能帮助开发者减轻开发成本,提高小程序的性能和品质;更有众多扶持和激励政策。
2021-11-26 - 首页开发实战
带你实战腾讯课堂首页开发,详解布局王者flex。 本节结束代码:https://share.weiyun.com/5Eqz7TV [视频]
2021-11-26 - 初识选择器、颜色和字体
初步认识“作为WXML和WXSS桥梁”的选择器,以及颜色与字体。 名片初始代码,点此领取:https://share.weiyun.com/5ASBOw3 [视频]
2021-11-26 - 传统前端和小程序前端对比
一、教学课件 教学PPT下载:https://share.weiyun.com/kYNlcejh 欢迎大家前往官网注册并下载工具体验:https://edu.weixin.qq.com/ 二、在线视频 [视频] 本课程将为大家讲解传统前端和小程序对比。 通过本课程的学习,将可以掌握两种前端技术的区别以及彼此的特点,让有传统前端开发经验的人员可以有一个开发思维上的过渡。
2024-09-03