- 小程序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 - 小程序webgl shader动效演示
[图片] [图片] 前言: 如果大家对webgl图形学感兴趣请留言回复,多的话我可能会写一些系列教程文章。 微信2.7版本新增webgl支持,开发版工具nightly已可调试 试用了一下 暂时发现几个问题: 1.移动端应避免大尺寸纹理 我的机器似乎2048以上就不支持, 没具体测试。 2.不支持 OES_standard_derivatives 拓展,这个拓展可以在webgl1下shader中使用以下函数 [代码]genType dFdx(genType) [代码]genType dFdy(genType) genType fwidth(genType) ,所以我们不去加载该拓展就好了 shader中也不要使用这些函数(这个问题在小游戏中也存在), 提到webgl,很多伙伴就想到Three.js, 实际上还有很多 像Babylon.js playcanvas scene.js等等 优秀的框架 整体使用下来,本人推荐playcanvas。 playcanvas整个框架逻辑完全照搬unity,有强大的网页编辑工具。比起Three.js有这更好的游戏功能支持,以及更优秀的资源脚本系统。 有丰富的拓展插件功能。 回到主题,小程序中使用webgl需要一个canvas <canvas type="webgl" id="application" style="width:100%;height:{{boxHeight}}px;" wx:if="{{canvasready}}"></canvas> const query = wx.createSelectorQuery() query.select('#application').node().exec((res) => { const canvas = res[0].node ; }) 通过createSelectorQuery拿到canvas实例,我们很多的方法都是 挂载在canvas上的, 像创建Image实例 使用canvas.createImage 像raf 使用 canvas.requestAnimationFrame 所以我们在改造引擎使得能够在微信小程序中工作,这些地方都要做相应的改动,我们改造的时候依然暴露出全局对象pc; 首先给canvas补上类dom元素的一些方法属性 canvas加上getBoundingClientRect方法,以及宽高 canvas.devicePixelRatio = systemInfo.pixelRatio; canvas.clientWidth = systemInfo.windowWidth; canvas.clientHeight = systemInfo.windowHeight; canvas.width = canvas.clientWidth; canvas.height = canvas.clientHeight; _defineProperty(canvas, 'getBoundingClientRect', { value: function () { return { top: 0, left: 0, width: canvas.width, height: canvas.height } }, writable: false, configurable: true }) pc对象上添加创建图片对象的方法,我这里加上instanceof用来判断资源类型(有时候需要区分canvas还是image) pc.createImage = function () { var image = canvas.createImage(); image.instanceof = 'HTMLImageElement'; return image; } app.canvas.requestAnimationFrame(app.tick); var application = new pc.Application(canvas, { assetPrefix: 'anonymous'}); application.setCanvasFillMode(pc.FILLMODE_FILL_WINDOW); application.setCanvasResolution(pc.RESOLUTION_AUTO); //在web中 Image配置crossOrigin 在小程序中似乎没有必要了 ,但是不影响我们依然加上 assetPrefix 创建一个相机,这个示例主要演示shader,不是3d模型,这里用正交投影相机 var camera = new pc.Entity(); camera.addComponent('camera', { nearClip: 0, farClip: 2, projection: pc.PROJECTION_ORTHOGRAPHIC, clearColor: new pc.Color(0, 0, 0) }); camera.setPosition(0, 0, 1); var CameraAspect = pc.createScript('cameraAspect'); CameraAspect.prototype.initialize = function () { this.currentOrthoHeight = this.entity.camera.orthoHeight; }; CameraAspect.prototype.update = function (t) { var canvas = this.app.graphicsDevice.canvas; var aspectRatio = canvas.width / canvas.height; var orthoHeight = pc.math.clamp(0.72 / aspectRatio, 1, 1.28); if (orthoHeight !== this.currentOrthoHeight) { this.entity.camera.orthoHeight = orthoHeight; this.currentOrthoHeight = orthoHeight; } }; camera.addComponent('script'); camera.script.create('cameraAspect'); application.root.addChild(camera); //创建一个平面 var plane = new pc.Entity(); plane.addComponent('model', {type: "plane"}); //TRS plane.setLocalPosition(0, -0.006,0); plane.setEulerAngles(90, 0, 0); plane.setLocalScale(1.54, 1.05, 2.61); //给平面引入脚本系统 plane.addComponent('script'); //shader脚本 cardManShader 如果在web端我们其实可以当成资源引入,可惜小程序中不支持动态脚本 var CardManShader = pc.createScript('cardManShader'); CardManShader.attributes.add('vs', { type: 'asset', assetType: 'shader', title: 'Vertex Shader' }); CardManShader.attributes.add('fs', { type: 'asset', assetType: 'shader', title: 'Fragment Shader' }); CardManShader.attributes.add('uTexture', { type: 'asset', assetType: 'texture', title: '火焰纹理' }); CardManShader.attributes.add('uNormalTexture', { type: 'asset', assetType: 'texture', title: '宝剑的remap 纹理' }); CardManShader.attributes.add('uFlowTexture', { type: 'asset', assetType: 'texture', title: 'wave纹理' }); CardManShader.attributes.add('uCardTexture', { type: 'asset', assetType: 'texture', title: '卡片纹理' }); CardManShader.attributes.add('uMaskTexture', { type: 'asset', assetType: 'texture', title: 'man遮罩纹理' }); CardManShader.attributes.add('uMainTexture', { type: 'asset', assetType: 'texture', title: 'man纹理' }); CardManShader.prototype.initialize = function () { this.time = 0; this.material = new pc.Material(); this.uTextureMap = this.uTexture.resource; this.uNormalTextureMap = this.uNormalTexture.resource; this.uFlowTextureMap = this.uFlowTexture.resource; this.uCardTextureMap = this.uCardTexture.resource; this.uMaskTextureMap = this.uMaskTexture.resource; this.uMainTextureMap = this.uMainTexture.resource; this.entity.model.model.meshInstances[0].material = this.material; this.createShader(); }; CardManShader.prototype.update = function (dt) { this.time += dt * 2; this.material.setParameter('uTime', this.time); }; CardManShader.prototype.createShader = function () { var app = this.app; var model = this.entity.model.model; var vertexShader = this.vs.resource; var fragmentShader = this.fs.resource; var shaderDefinition = { attributes: { aPosition: pc.SEMANTIC_POSITION, aNormal: pc.SEMANTIC_NORMAL, aUv: pc.SEMANTIC_TEXCOORD0 }, vshader: vertexShader, fshader: fragmentShader }; var gd = app.graphicsDevice; this.shader = new pc.Shader(gd, shaderDefinition); this.material.shader = this.shader; this.material.setParameter('uTime', 0); this.material.setParameter('uTexture', this.uTextureMap); this.material.setParameter('uNormalTexture', this.uNormalTextureMap); this.material.setParameter('uFlowTexture', this.uFlowTextureMap); this.material.setParameter('uCardTexture', this.uCardTextureMap); this.material.setParameter('uMaskTexture', this.uMaskTextureMap); this.material.setParameter('uMainTexture', this.uMainTextureMap); }; CardManShader.prototype.updateShader = function (t1, t2, t3, t4, t5, t6) { if (t1) this.uTexture = t1; this.uTextureMap = this.uTexture.resource; if(t2) this.uNormalTexture = t2; this.uNormalTextureMap = this.uNormalTexture.resource; if (t3) this.uFlowTexture = t3; this.uFlowTextureMap = this.uFlowTexture.resource; if(t4) this.uCardTexture = t4; this.uCardTextureMap = this.uCardTexture.resource; if(t5) this.uMaskTexture = t5; this.uMaskTextureMap = this.uMaskTexture.resource; if (t6) this.uMainTexture = t6; this.uMainTextureMap = this.uMainTexture.resource; var material = this.entity.model.model.meshInstances[0].material; material.setParameter('uTexture', this.uTextureMap); material.setParameter('uNormalTexture', this.uNormalTextureMap); material.setParameter('uFlowTexture', this.uFlowTextureMap); material.setParameter('uCardTexture', this.uCardTextureMap); material.setParameter('uMaskTexture', this.uMaskTextureMap); material.setParameter('uMainTexture', this.uMainTextureMap); }; //给shader脚本 加上着色器 以及贴图资源 var vsglsl = new pc.Asset("vs", "shader"); vsglsl.resource = `precision ${application.graphicsDevice.precision} float; attribute vec4 aPosition; attribute vec3 aNormal; attribute vec2 aUv; uniform mat4 matrix_viewProjection; uniform mat4 matrix_model; uniform mat4 matrix_view; uniform mat3 matrix_normal; uniform vec3 uLightPos; // Color to fragment program varying float vertOutTexCoord; varying vec2 texCoord; void main(void) { mat4 modelView = matrix_view * matrix_model; mat4 modelViewProj = matrix_viewProjection * matrix_model; vec3 eyeNormal = normalize(matrix_normal * aNormal); vec4 vertexPos = modelView * aPosition; vec3 vertexEyePos = vertexPos.xyz / vertexPos.w; vec3 lightDir = normalize(uLightPos - vertexEyePos); vertOutTexCoord = max(0.0, dot(eyeNormal, lightDir)); texCoord = aUv; gl_Position = modelViewProj * aPosition; }`; var fsglsl = new pc.Asset("fs", "shader"); fsglsl.resource = `precision ${application.graphicsDevice.precision} float; uniform sampler2D uTexture; uniform sampler2D uNormalTexture; uniform sampler2D uFlowTexture; uniform sampler2D uCardTexture; uniform sampler2D uMaskTexture; uniform sampler2D uMainTexture; uniform float uDistortPower; uniform float uTime; varying float vertOutTexCoord; varying vec2 texCoord; vec3 unpackNormal(sampler2D nmap, vec2 uv) { return texture2D(nmap,uv).xyz*2.0-vec3(1.0); } vec3 unpackNormal(vec4 color) { return color.xyz*2.0-vec3(1.0); } vec3 packNormal(vec3 n) { return (n+vec3(1.0))*0.5; } vec2 distort(float mask,sampler2D distTex, vec2 distUV, float distPower) { vec4 distColor = texture2D(distTex, distUV + vec2(uTime) * 0.5); vec2 offsetUV = (mask * unpackNormal(distColor) * distPower).st; return offsetUV; } vec2 rotate(vec2 uv, vec4 param,float time) { float cosx = cos(param.z + param.w * time); float sinx = sin(param.z + param.w * time); mat2 m2 = mat2(cosx, sinx, -sinx, cosx); return m2 * (uv.xy - param.xy) + param.xy; } vec2 UV_RotateAround(vec2 center,vec2 uv,float rad){ vec2 fuv = uv - center; mat2 ma = mat2(cos(rad),sin(rad),-sin(rad),cos(rad)); fuv = ma * fuv + center; return fuv; } vec2 UV_STD2Rect(vec2 uv,vec4 rect){ uv.x = mix(rect.x,rect.x+rect.z, uv.x); uv.y = mix(rect.y,rect.y+rect.w, uv.y); return uv; } vec4 remap(sampler2D remapTex,vec2 uv){ vec2 uv_remap2 = uv; vec4 color_remap2 = texture2D(remapTex,uv_remap2); if(color_remap2.b >= 0.5) { color_remap2 = vec4(0,color_remap2.gba); } return color_remap2; } const vec4 _Color_image1 = vec4(1.,1.,1.,1.); vec2 imagenodeBase(vec2 uv_image,vec4 uv_tint,vec3 uv_move,float uv_rotate){ vec2 tilling = uv_tint.xy; vec2 offset = uv_tint.zw; vec2 move = uv_move.xy; float moveFactor = uv_move.z; float rotaterad = uv_rotate; vec2 center_image = vec2(0.5,0.5); uv_image = uv_image-center_image; uv_image = uv_image+offset; //trs uv_image = uv_image+move*moveFactor; uv_image = UV_RotateAround(vec2(0.,0.),uv_image,rotaterad); uv_image = uv_image/tilling; // vec2 dir_image = uv_image/length(uv_image); // uv_image = uv_image-dir_image*vec2(0.,0.)*(_Time.y); //spin // uv_image = UV_RotateAround(vec2(0.,0.),uv_image,0.*(_Time.y)); //radial uv_image = uv_image + center_image; return uv_image; } vec2 uvnode(sampler2D _waveTex,vec2 uv){ vec2 _Time = vec2(uTime,uTime); vec2 tilling = vec2(0.60546,0.60913); vec2 offset = vec2(0.,4.17232); vec2 move = vec2(0.07617,0.); float moveFactor = _Time.y; float rotaterad = 0.; vec2 ruv = vec2(0.,0.1503901); vec2 guv = vec2(0.24501,0.00114); vec2 buv = vec2(0.,0.); vec2 auv = vec2(0.,0.); vec2 uv_uv = uv; uv_uv = imagenodeBase(uv_uv, vec4(tilling,offset),vec3(move,moveFactor),rotaterad); vec4 rect_uv = vec4(1,1,1,1); vec4 color_uv = texture2D(_waveTex,uv_uv); uv_uv = -(color_uv.r*ruv + color_uv.g*guv + color_uv.b*buv + color_uv.a*auv); return uv_uv; } vec2 uvnodehair(sampler2D _waveTex,vec2 uv){ vec2 _Time = vec2(uTime,uTime); vec2 tilling = vec2(0.34375,0.3339844); vec2 offset = vec2(0.2,0.); vec2 move = vec2(0.,-0.1328125); float moveFactor = _Time.y; float rotaterad = 0.; vec2 ruv = vec2(-0.02734375,0.02539063); vec2 guv = vec2(0.02148438,0.02539063); vec2 buv = vec2(0.,0.); vec2 auv = vec2(0.,0.); vec2 uv_uv = uv; uv_uv = imagenodeBase(uv_uv, vec4(tilling,offset),vec3(move,moveFactor),rotaterad ); vec4 rect_uv = vec4(1,1,1,1); vec4 color_uv = texture2D(_waveTex,uv_uv); uv_uv = -(color_uv.r*ruv + color_uv.g*guv + color_uv.b*buv + color_uv.a*auv); return uv_uv; } vec4 imagenode(sampler2D _flameTex, vec4 color_remap,vec2 newuv,vec2 olduv){ vec2 _Time = vec2(uTime,uTime); vec2 tilling = vec2(0.5,1.); vec2 offset = vec2(0.,0.); vec2 move = vec2(-0.06640625,0); float moveFactor = _Time.y; float rotaterad = 0.; vec2 uv_image = color_remap.rg; uv_image = imagenodeBase(uv_image,vec4(tilling,offset),vec3(move,moveFactor),rotaterad); uv_image = uv_image + newuv*1.*((1.)); vec4 rect_image = vec4(1.,1.,1.,1.); vec4 color_image = texture2D(_flameTex,uv_image); //color_image = color_image * _Color_image3; //blendadd color_image = vec4(color_image.rgb,color_image.a*color_remap.a); return color_image; } void main(void) { vec2 _Time = vec2(uTime,uTime); // float v = vertOutTexCoord; // v = float(int(v * 6.0)) / 6.0; vec4 color_xman_mask0 = texture2D(uMaskTexture,texCoord); vec2 uv = texCoord; vec4 color = texture2D (uTexture, uv); vec2 color_ROOTUV = imagenodeBase(texCoord, vec4(0.9550781,0.5,0.02246094,-0.2177734), vec3(0.,0.,uTime),0.); vec4 remapuv = remap(uNormalTexture,color_ROOTUV); vec2 newuv = uvnode(uFlowTexture,color_ROOTUV); vec4 flowcolor = imagenode(uTexture,remapuv,newuv,color_ROOTUV); vec2 newuvhair = uvnodehair(uFlowTexture,color_ROOTUV); vec4 offsetx_color_xman_mask0 = texture2D(uMaskTexture,texCoord + vec2(-0.1,-0.02)); vec2 color_ROOT2 = color_ROOTUV + newuvhair *1.*((1.)) * offsetx_color_xman_mask0.b; vec4 color_ROOT = texture2D(uMainTexture,color_ROOT2); color = mix(color_ROOT,vec4(flowcolor.rgb,1.),clamp(flowcolor.a*1.*((1.)),0.,1.)); vec4 cardcolor = texture2D(uCardTexture,texCoord); color = mix(cardcolor,vec4(color.rgb,1.),clamp(color.a*1.*((1.)) * color_xman_mask0.r,0.,1.)); //gl_FragColor = color_ROOT; gl_FragColor = color; }` var uTextureAsset = new pc.Asset("uTexture", "texture", { url: "http://p1.qhimgs3.com/t01cd1ec98a32680eba.png" }); var uNormalTextureAsset = new pc.Asset("uNormalTexture", "texture", { url: "http://p1.qhimgs3.com/t01396cc020f83752f8.png" }); var uFlowTextureAsset = new pc.Asset("uFlowTexture", "texture", { url: "http://p1.qhimgs3.com/t01a07e2120f6d6e227.png" }); var uCardTextureAsset = new pc.Asset("uCardTexture", "texture", { url: "http://p1.qhimgs3.com/t0106a31ec8b61c2cb5.png" }); var uMaskTextureAsset = new pc.Asset("uMaskTexture", "texture", { url: "http://p1.qhimgs3.com/t0163659206d15e5cb5.jpg" }); var uMainTextureAsset = new pc.Asset("uMainTexture", "texture", { url: "http://p1.qhimgs3.com/t01dbc7e56d6af1643d.png" }); application.assets.add(uTextureAsset); application.assets.add(uNormalTextureAsset); application.assets.add(uFlowTextureAsset); application.assets.add(uCardTextureAsset); application.assets.add(uMaskTextureAsset); application.assets.add(uMainTextureAsset); plane.script.on('create:cardManShader', function (_shader) { _shader.vs = vsglsl; _shader.fs = fsglsl; _shader.uTexture = uTextureAsset; _shader.uNormalTexture = uNormalTextureAsset; _shader.uFlowTexture = uFlowTextureAsset; _shader.uCardTexture = uCardTextureAsset; _shader.uMaskTexture = uMaskTextureAsset; _shader.uMainTexture = uMainTextureAsset; }) plane.script.create('cardManShader'); //shader application.assets.load(uTextureAsset); application.assets.load(uNormalTextureAsset); application.assets.load(uFlowTextureAsset); application.assets.load(uCardTextureAsset); application.assets.load(uMaskTextureAsset); application.assets.load(uMainTextureAsset); application.assets.on("load",function(){ plane.script.cardManShader.updateShader(); }) application.root.addChild(plane); //最后再开始tick ,提前开始tick会在真机上有未查明的问题 application.start(); 这样就能在小程序中展现我们动图中的特效了。 具体代码,稍后提交到github。请关注songshujitu 结尾打个广告 下面是我们目前创业在做的小程序,大家进来玩下吧 [图片]
2019-05-30