- Scope 参数错误或没有 Scope 权限,错误码:10005
登录提示:Scope 参数错误或没有 Scope 权限 [图片] 排查方案: 1:使用的是订阅号,订阅号没有权限使用网页授权,详细可参考接口权限:https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Explanation_of_interface_privileges.html 2:需要已认证的服务号 3:网页授权回调域名填写错误 4:Scope参数顺序不对 5:服务号不支持扫码登录,要网站应用才支持:https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html 6:snsapi_userinfo的接口权限被封
2021-07-07 - 反馈文档链接、信息过期
下图为scope等于snsapi_userinfo时的授权页面: https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html 在微信浏览器中打开时,是登陆微信开放社区的页面,并且截图用微信扫一扫这个二维码时,提示“该二维码仅支持微信扫一扫识别,不支持长按图片识别或手机相册扫码识别等其他方式” 请问网站app 在微信浏览器中打开时,如何实现静默授权?网页app的扫码登陆过程会对比电脑端有何变化? [图片][图片]
04-12 - IOS 微信更新到8.0.30 之后webgl canvas 无触点信息
在iOS 升级到最新的微信版本后,webgl canvas 无法在真机上得到触点信息(touches filed 为空)。 在开发者工具中点击: [图片] 同样的程序在真机调试中: [图片]
2022-12-30 - 小程序app.onLaunch与page.onLoad异步问题的最佳实践
场景: 在小程序中大家应该都有这样的场景,在onLaunch里用wx.login静默登录拿到code,再用code去发送请求获取token、用户信息等,整个过程都是异步的,然后我们在业务页面里onLoad去用的时候异步请求还没回来,导致没拿到想要的数据,以往要么监听是否拿到,要么自己封装一套回调,总之都挺麻烦,每个页面都要写一堆无关当前页面的逻辑。 直接上终极解决方案,公司内部已接入两年很稳定: 1.可完美解决异步问题 2.不污染原生生命周期,与onLoad等钩子共存 3.使用方便 4.可灵活定制异步钩子 5.采用监听模式实现,接入无需修改以前相关逻辑 6.支持各种小程序和vue架构 。。。 //为了简洁明了的展示使用场景,以下有部分是伪代码,请勿直接粘贴使用,具体使用代码看Github文档 //app.js //globalData提出来声明 let globalData = { // 是否已拿到token token: '', // 用户信息 userInfo: { userId: '', head: '' } } //注册自定义钩子 import CustomHook from 'spa-custom-hooks'; CustomHook.install({ 'Login':{ name:'Login', watchKey: 'token', onUpdate(token){ //有token则触发此钩子 return !!token; } }, 'User':{ name:'User', watchKey: 'userInfo', onUpdate(user){ //获取到userinfo里的userId则触发此钩子 return !!user.userId; } } }, globalData) // 正常走初始化逻辑 App({ globalData, onLaunch() { //发起异步登录拿token login((token)=>{ this.globalData.token = token //使用token拿用户信息 getUser((user)=>{ this.globalData.user = user }) }) } }) //关键点来了 //Page.js,业务页面使用 Page({ onLoadLogin() { //拿到token啦,可以使用token发起请求了 const token = getApp().globalData.token }, onLoadUser() { //拿到用户信息啦 const userInfo = getApp().globalData.userInfo }, onReadyUser() { //页面初次渲染完毕 && 拿到用户信息,可以把头像渲染在canvas上面啦 const userInfo = getApp().globalData.userInfo // 获取canvas上下文 const ctx = getCanvasContext2d() ctx.drawImage(userInfo.head,0,0,100,100) }, onShowUser() { //页面每次显示 && 拿到用户信息,我要在页面每次显示的时候根据userInfo走不同的逻辑 const userInfo = getApp().globalData.userInfo switch(userInfo.sex){ case 0: // 走女生逻辑 break case 1: // 走男生逻辑 break } } }) 具体文档和Demo见↓ Github:https://github.com/1977474741/spa-custom-hooks 祝大家用的愉快,记得star哦
2023-04-23 - BackgroundAudioManager小程序什么时候支持MIDI音频格式播放?
做了一款自动生成音乐的小程序,使用wx.getBackgroundAudioManager()获取音频,由于不支持Midi格式, 所以接口编曲后要进行转码返回m4a、aac等格式的音频,由于返回音频不是一次性的,是不间断的更新src音频地址,但一个音频文件BackgroundAudioManager需要加载6-10s才会播放,而且aac等格式音频文件较大3-5MB,一个20s左右的midi文件大概也就1kb,不影响体验的流畅性且这个流程下来不会花费太多流量,目前由于加载速度太慢导致两个音频中间间隔较长,导致用户体验中断(反馈以为小程序卡了或中间出现什么问题),影响到了小程序的使用。 所以,想问下小程序能否支持midi格式的音频播放,大概什么时候会支持,这对我的小程序最终效果起到很关键的作用。
2021-04-21 - 小程序音视频合成初探
小程序音视频 最近使用了一下微信音视频相关 api, 这绝对是一件振奋人心的事, 让视频的合成在小程序端就能完成, 以下是我使用这些 api 的一些记录。 以下代码片段可以直接在开发者工具中预览 视频合成 音视频合成主要用到 [代码]wx.createMediaContainer()[代码] 方法, 该方法会返回一个 [代码]MediaContainer[代码] 对象, 可以将视频源传入到容器中, 对视频轨和音频轨进行一些操作。 下面以合成视频为例: [代码]const mediaContainer = wx.createMediaContainer(); const { tempFilePath } = this.data; // 本地视频文件地址 // 将视频源传入轨道当中 mediaContainer.extractDataSource({ source: tempFilePath, success: (res) => { // 返回的结果中的 tracks 是一个类数组对象, 第一项是音频轨道, 第二项是视频轨道 const [audioTrack, mediaTrack] = res.tracks; mediaContainer.addTrack(mediaTrack); // 将视频轨道加入到待合成容器中 mediaTrack.slice(1000, 5000); // 截取视频轨道中第 1-5 秒的视频 mediaContainer.addTrack(audioTrack); // 将音频轨道加入到待合成容器中 audioTrack.slice(1000, 5000); // 截取音频轨道中第 1-5 秒的视频 // 导出合成容器中的音频和视频 mediaContainer.export({ success: (res) => { // 拿到导出之后的视频 console .log(res.tempFilePath); }, }); }, }); [代码] 视频解码和录制 如果想要给视频加滤镜和贴图可以采用, [代码]VideoDecoder[代码] + [代码]MediaRecorder[代码] + [代码]WebGL[代码] 的方式, 通过 [代码]VideoDecoder[代码] 将视频解码, 获取视频的每一帧画面, 再绘制到 [代码]canvas[代码] 上, 再通过 [代码]glsl[代码] 着色器给画面加滤镜。同时用 [代码]MediaRecorder[代码] 去录制 [代码]canvas[代码] 上的画面, 最后可以导出一段视频。 以下是将一个视频的前十秒加上黑白滤镜合成出来的主要代码: [代码]let w = 300 let h = 200 const vs = ` attribute vec3 aPos; attribute vec2 aVertexTextureCoord; varying highp vec2 vTextureCoord; void main(void){ gl_Position = vec4(aPos, 1); vTextureCoord = aVertexTextureCoord; } ` const fs = ` varying highp vec2 vTextureCoord; uniform sampler2D uSampler; #ifdef GL_ES precision lowp float; #endif void main(void) { vec4 color = texture2D(uSampler, vTextureCoord); float gray = 0.2989*color.r + 0.5870*color.g + 0.1140*color.b; gl_FragColor = vec4(gray, gray, gray, color.a); } ` const vertex = [ -1, -1, 0.0, 1, -1, 0.0, 1, 1, 0.0, -1, 1, 0.0 ] const vertexIndice = [ 0, 1, 2, 0, 2, 3 ] const texCoords = [ 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0 ] function createShader(gl, src, type) { const shader = gl.createShader(type) gl.shaderSource(shader, src) gl.compileShader(shader) if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { console.error('Error compiling shader: ' + gl.getShaderInfoLog(shader)) } return shader } function createRenderer(canvas, width, height) { const gl = canvas.getContext("webgl") if (!gl) { console.error('Unable to get webgl context.') return } const info = wx.getSystemInfoSync() gl.canvas.width = width //info.pixelRatio * width gl.canvas.height = height // info.pixelRatio * height gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight) const vertexShader = createShader(gl, vs, gl.VERTEX_SHADER) const fragmentShader = createShader(gl, fs, gl.FRAGMENT_SHADER) const program = gl.createProgram() gl.attachShader(program, vertexShader) gl.attachShader(program, fragmentShader) gl.linkProgram(program) if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { console.error('Unable to initialize the shader program.') return } gl.useProgram(program) const texture = gl.createTexture() gl.activeTexture(gl.TEXTURE0) gl.bindTexture(gl.TEXTURE_2D, texture) gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true) gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) gl.bindTexture(gl.TEXTURE_2D, null) buffers.vertexBuffer = gl.createBuffer() gl.bindBuffer(gl.ARRAY_BUFFER, buffers.vertexBuffer) gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertex), gl.STATIC_DRAW) buffers.vertexIndiceBuffer = gl.createBuffer() gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers.vertexIndiceBuffer) gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(vertexIndice), gl.STATIC_DRAW) const aVertexPosition = gl.getAttribLocation(program, 'aPos') gl.vertexAttribPointer(aVertexPosition, 3, gl.FLOAT, false, 0, 0) gl.enableVertexAttribArray(aVertexPosition) buffers.trianglesTexCoordBuffer = gl.createBuffer() gl.bindBuffer(gl.ARRAY_BUFFER, buffers.trianglesTexCoordBuffer) gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(texCoords), gl.STATIC_DRAW) const vertexTexCoordAttribute = gl.getAttribLocation(program, "aVertexTextureCoord") gl.enableVertexAttribArray(vertexTexCoordAttribute) gl.vertexAttribPointer(vertexTexCoordAttribute, 2, gl.FLOAT, false, 0, 0) const samplerUniform = gl.getUniformLocation(program, 'uSampler') gl.uniform1i(samplerUniform, 0) return (arrayBuffer, width, height) => { gl.bindTexture(gl.TEXTURE_2D, texture) 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) } } wx.createSelectorQuery().select('#video').context(res => { const video = this.video = res.context // 获取 VideoContext video.pause() const decoder = wx.createVideoDecoder() // 创建解码器 const dpr = 1 const webgl = wx.createOffscreenCanvas() const render = createRenderer(webgl, w, h) console.log('webgl', webgl.id, w, h, dpr, webgl.width, webgl.height) const fps = 25 const recorder = wx.createMediaRecorder(webgl, { fps, videoBitsPerSecond: 3000, timeUpdateInterval: 30 }) const startTime = Date.now() let counter = 0 let loopStopped = false let timeCnt = 0 setTimeout(() => { recorder.stop(); decoder.stop(); }, 3000); function loop() { const renderTime = (counter + 1) * (1000 / fps) if (loopStopped || renderTime > 10100) { console.log('recorder stop.', timeCnt, Date.now() - startTime) recorder.stop() return } const ts = Date.now() const imageData = decoder.getFrameData() if (imageData) { render(new Uint8Array(imageData.data), w * dpr, h * dpr) timeCnt += Date.now() - ts counter++ } console.log('render end', counter, Date.now() - ts); recorder.requestFrame(loop) } recorder.on('start', () => { console.log('start render') loopStopped = false loop() }) recorder.on('timeupdate', ({ currentTime }) => { console.log('timeupdate', currentTime) }) recorder.on('stop', (res) => { console.log('recorder finished.', timeCnt, Date.now() - startTime, res) this.setData({ distSrc: res.tempFilePath }) recorder.destroy() }) decoder.on('start', () => { console.log('decoder start 2', decoder.seek) decoder.on('seek', () => { console.log('decoder seeked') recorder.start() }) decoder.seek(0) }) decoder.start({ source: this.data.src, // 这里是一个视频的本地路径, 可通过 wx.chooseVideo 获取 }) }).exec() [代码] 不过此时录制出来的视频是没有声音, 需要通过上面讲到的 [代码]MediaContainer[代码] 截取前 10s 的音频, 将录制出的视频和音频合成得到一段完整的视频。[代码]CanvasRenderingContext2D[代码] 的 [代码]drawImage[代码] 方法 2.10.0 起支持传入通过 [代码]SelectorQuery[代码] 获取的 video 对象, 所以对视频的操作可以先使用 canvas 预览, 再使用 [代码]MediaRecorder[代码] 进行录制。 小程序音视频相关 api 就介绍到这儿, 更多具体的文档, 可以参考微信官方文档。这些 api 大大方便了开发者对音视频的处理, 也期待更多小程序音视频 api 的开放。
2020-07-01 - 小程序中tf插件的tf.browser.fromPixels()不支持canvas创建的image?
pixels passed to tf.browser.fromPixels() must be either an HTMLVideoElement, HTMLImageElement, HTMLCanvasElement, ImageData in browser, or OffscreenCanvas, ImageData in webworker or {data: Uint32Array, width: number, height: number}, but was ImageData Error: pixels passed to tf.browser.fromPixels() must be either an HTMLVideoElement, HTMLImageElement, HTMLCanvasElement, ImageData in browser, or OffscreenCanvas, ImageData in webworker or {data: Uint32Array, width: number, height: number}, but was ImageData
2020-11-07 - 请使用1.4.4及以上版本基础库 ?
[图片][图片] 如图 基础调试库设置的是2.17.0 提示请使用1.4.4 及以上版本
2021-06-08 - 使用wx.setStorageSync缓存的数据被自动清除
使用wx.setStorageSync缓存token,会被自动清除掉,请问有遇到这种情况的吗,如何解决的?
2019-06-17