HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

mui实现每个跳转页面刷新数据的的方法

第一步给自己的跳转页面方法加个自定义方法


第二步修改mui.js里面源代码


第三步页面监听方法做自己处理

这样可以处理数据刷新的问题,免得一个一个页面mui.fire写自己都忘了怎么调用,毕竟一个项目页面多了,直接改源码,每个页面监听一下自己的方法处理业务,还要顺便吐槽一下官方文档太少了,建议大家直接看5+api和mui.js里面源码,大部分问题就能解决了,官方文档是看不出来什么的

继续阅读 »

第一步给自己的跳转页面方法加个自定义方法


第二步修改mui.js里面源代码


第三步页面监听方法做自己处理

这样可以处理数据刷新的问题,免得一个一个页面mui.fire写自己都忘了怎么调用,毕竟一个项目页面多了,直接改源码,每个页面监听一下自己的方法处理业务,还要顺便吐槽一下官方文档太少了,建议大家直接看5+api和mui.js里面源码,大部分问题就能解决了,官方文档是看不出来什么的

收起阅读 »

第一章(序):MUI集成个推、信鸽推送,华为推送、小米推送、魅族推送

个推SDK

此教程针对使用MUI开发的APP,(若使用uni-app,则按照官方文档即可,其实他们也是做了我说的这些工作)

MUI官方已经停止维护,因此想要APP离线推送必看此教程

APP离线推送是业界一大难题,特别是Android机,国内手机厂商都阉割掉谷歌的推送服务,同时对后台进程严格限制,某些厂商甚至严格限制APP相互包活机制,所以难以保证用户能收到离线消息;
但APP离不开离线推送,像微信、QQ等大厂被国内各手机厂商加入系统级白名单,能保证实时到达率,对于小厂来说只能使用我说的办法。
教程要点:
1、APP需要离线打包;
2、集成个推(后公司要求改为集成了信鸽推送);
3、集成华为推送;
4、集成小米推送;
5、集成魅族推送;
此教程,需要有MUI开发经验,需要一点APP原生开发能力,不过按照我说的一步一来即可;

未完待续....

,若你急需此功能,加QQ:944515900,帮你解决,给个红包犒劳一下就行;

继续阅读 »

此教程针对使用MUI开发的APP,(若使用uni-app,则按照官方文档即可,其实他们也是做了我说的这些工作)

MUI官方已经停止维护,因此想要APP离线推送必看此教程

APP离线推送是业界一大难题,特别是Android机,国内手机厂商都阉割掉谷歌的推送服务,同时对后台进程严格限制,某些厂商甚至严格限制APP相互包活机制,所以难以保证用户能收到离线消息;
但APP离不开离线推送,像微信、QQ等大厂被国内各手机厂商加入系统级白名单,能保证实时到达率,对于小厂来说只能使用我说的办法。
教程要点:
1、APP需要离线打包;
2、集成个推(后公司要求改为集成了信鸽推送);
3、集成华为推送;
4、集成小米推送;
5、集成魅族推送;
此教程,需要有MUI开发经验,需要一点APP原生开发能力,不过按照我说的一步一来即可;

未完待续....

,若你急需此功能,加QQ:944515900,帮你解决,给个红包犒劳一下就行;

收起阅读 »

关于接触plus以来的一些经验

HTML5+

1.一定要android和IOS一起来测试,否则弄了半天,在一个运行的好好的,在另一个里便不行了,这样会浪费很多时间。
2.能在浏览器里弄得了的就在浏览器里搞,否则在调试的时候最大的一个特点就是在很多情况下不指明错误在哪儿。(在第几行)没有浏览器开发工具那种查看元素的功能。

  1. 很多API还不健全,特别是搞视频的时候,走了不少弯路,踩了不少坑。比如video, 缓存等等,很多东西都不能自定义。很多插件还亟待完善。
  2. 资料还不是很全,大部分时候搜的时候还是搜不到,论坛里很多时候没人搭理你。
继续阅读 »

1.一定要android和IOS一起来测试,否则弄了半天,在一个运行的好好的,在另一个里便不行了,这样会浪费很多时间。
2.能在浏览器里弄得了的就在浏览器里搞,否则在调试的时候最大的一个特点就是在很多情况下不指明错误在哪儿。(在第几行)没有浏览器开发工具那种查看元素的功能。

  1. 很多API还不健全,特别是搞视频的时候,走了不少弯路,踩了不少坑。比如video, 缓存等等,很多东西都不能自定义。很多插件还亟待完善。
  2. 资料还不是很全,大部分时候搜的时候还是搜不到,论坛里很多时候没人搭理你。
收起阅读 »

UniPush的推送到达率太低

unipush uniapp

最近我将APP的推送从个推改成了UniPush,可是我发现推送的到达率好低呀,我每次重启一下APP之后的第一条推送可能收到,后续的推送一条也收不到了。。。

其中java中的核心方法我贴出来了,各位大佬可以帮忙瞅瞅,有什么大问题吗?

/**  
     * intent模板  
     */  
    private static String intentTemplate = "intent:#Intent;action=android.intent.action.oppopush;component=%s/io.dcloud.PandoraEntry;S.UP-OL-SU=true;S.title=%s;S.content=%s;S.payload=%s;end";  

/**  
     * 获取管理APP Intent信息  
     * @param pushMessage 管理员推送消息  
     * @return intent字符串  
     */  
    private static String getManagerIntent(PushMessage pushMessage) {  
        return String.format(intentTemplate, managerAppProperties.getPackageName(), pushMessage.getTitle(), pushMessage.getContent(), ObjectUtils.toJson(pushMessage.getPayload()));  
    }  

    /**  
     * 获取司机APP Intent信息  
     * @param pushMessage 司机端推送的消息  
     * @return intent 字符串  
     */  
    private static String getDriverIntent(PushMessage pushMessage) {  
        return String.format(intentTemplate, driverAppProperties.getPackageName(), pushMessage.getTitle(), pushMessage.getContent(), ObjectUtils.toJson(pushMessage.getPayload()));  
    }  

   /**  
     * 创建透传模板  
     * @param pushMessage 透传消息对象  
     * @param manager 是否是管理员透传消息  
     * @return 透传模板  
     */  
    private static TransmissionTemplate createTransmissionTemplate(PushMessage pushMessage, boolean manager) {  
        // 创建模板  
        TransmissionTemplate template = new TransmissionTemplate();  
        if (manager) {  
            template.setAppId(managerAppProperties.getAppId());  
            template.setAppkey(managerAppProperties.getAppKey());  
        } else {  
            template.setAppId(driverAppProperties.getAppId());  
            template.setAppkey(driverAppProperties.getAppKey());  
        }  

        template.setTransmissionType(2);  
        template.setTransmissionContent(ObjectUtils.toJson(pushMessage));  

        Notify notify = new Notify();  
        notify.setTitle(pushMessage.getTitle());  
        notify.setContent(pushMessage.getContent());  
        notify.setIntent(manager ? getManagerIntent(pushMessage) : getDriverIntent(pushMessage));  
        notify.setType(GtReq.NotifyInfo.Type._intent);  

        template.set3rdNotifyInfo(notify);  
        return template;  
    }  

...
继续阅读 »

最近我将APP的推送从个推改成了UniPush,可是我发现推送的到达率好低呀,我每次重启一下APP之后的第一条推送可能收到,后续的推送一条也收不到了。。。

其中java中的核心方法我贴出来了,各位大佬可以帮忙瞅瞅,有什么大问题吗?

/**  
     * intent模板  
     */  
    private static String intentTemplate = "intent:#Intent;action=android.intent.action.oppopush;component=%s/io.dcloud.PandoraEntry;S.UP-OL-SU=true;S.title=%s;S.content=%s;S.payload=%s;end";  

/**  
     * 获取管理APP Intent信息  
     * @param pushMessage 管理员推送消息  
     * @return intent字符串  
     */  
    private static String getManagerIntent(PushMessage pushMessage) {  
        return String.format(intentTemplate, managerAppProperties.getPackageName(), pushMessage.getTitle(), pushMessage.getContent(), ObjectUtils.toJson(pushMessage.getPayload()));  
    }  

    /**  
     * 获取司机APP Intent信息  
     * @param pushMessage 司机端推送的消息  
     * @return intent 字符串  
     */  
    private static String getDriverIntent(PushMessage pushMessage) {  
        return String.format(intentTemplate, driverAppProperties.getPackageName(), pushMessage.getTitle(), pushMessage.getContent(), ObjectUtils.toJson(pushMessage.getPayload()));  
    }  

   /**  
     * 创建透传模板  
     * @param pushMessage 透传消息对象  
     * @param manager 是否是管理员透传消息  
     * @return 透传模板  
     */  
    private static TransmissionTemplate createTransmissionTemplate(PushMessage pushMessage, boolean manager) {  
        // 创建模板  
        TransmissionTemplate template = new TransmissionTemplate();  
        if (manager) {  
            template.setAppId(managerAppProperties.getAppId());  
            template.setAppkey(managerAppProperties.getAppKey());  
        } else {  
            template.setAppId(driverAppProperties.getAppId());  
            template.setAppkey(driverAppProperties.getAppKey());  
        }  

        template.setTransmissionType(2);  
        template.setTransmissionContent(ObjectUtils.toJson(pushMessage));  

        Notify notify = new Notify();  
        notify.setTitle(pushMessage.getTitle());  
        notify.setContent(pushMessage.getContent());  
        notify.setIntent(manager ? getManagerIntent(pushMessage) : getDriverIntent(pushMessage));  
        notify.setType(GtReq.NotifyInfo.Type._intent);  

        template.set3rdNotifyInfo(notify);  
        return template;  
    }  

...
收起阅读 »

微信小程序转换uni-app详细指南、小程序转uni-app转换器、wepy转uni-app

小程序转为uni_app 小程序转成uni_app 小程序转uni_app 小程序转换

号外:推荐使用HBuilderX插件https://ext.dcloud.net.cn/plugin?id=2656,直接转换小程序到uni-app

本文首先分享转换步骤和原理,文末会分享三方开发者制作的 小程序转uni-app的转换器wepy转uni-app转换器

小程序转换uni-app的步骤

微信小程序的语法,其实是vue.js语法的裁剪定制版,在数据绑定、自定义组件等很多方面都有相似之处。
以下是一个小程序源码转换步骤指南:

客户端代码转换

  1. 新建一个uni-app项目,把之前的app.js、app.wxss的代码,挪到app.vue里,分别放到script和style节点下面
    如果其中有globalData等全局变量或方法,也直接放到app.vue的script下
    export default {    
        globalData: {    
            text: 'text'    
        },    
        onLaunch: function() {    
            console.log('App Launch')    
        }  
    }  

    读取globalData或赋值的方法是getApp().globalData.text = 'test'

  2. 转换app.json为pages.json,把每个小程序page目录下的index.json(或页面名称对应的json)里的配置取出来,放到pages.json的style下
  3. pages下每个页面目录放一个vue空文件模板
  4. 把之前页面的wxss内容复制到vue文件的style中,无需改动
  5. 把之前页面的js内容复制到vue文件的script中,然后执行如下改动
    • 5.1 之前js里面的data,放到新的data return下
      之前
      Page({  
      data: {  
      show1: false  
      }  
      })

      现在

      <script>  
      export default {  
      data() {  
          return {  
              show1: false  
          }  
      }  
      }  
      </script>
    • 5.2 之前js里面的自定义方法,放到新的method下
      之前
      Page({  
      toggleActionSheet1() {  
      this.setData({show1: true});  
      }  
      })

      现在

      <script>  
      export default {  
      methods: {  
          toggleActionSheet1() {  
              this.show1 = true  
          }  
      }  
      }  
      </script>
    • 5.3 之前js里面的生命周期函数onLoad、onShow等,直接放到export default下
      之前
      Page({  
      onLoad() {  
      console.log("page load");  
      }  
      })

      现在

      <script>  
      export default {  
      onLoad() {  
          console.log("page load");  
      }  
      }  
      </script>
    • 5.4 setdata的处理方式
    • 方式一:从 this.setData({loading: false,areaList: response.data.data}) 改为 this.loading = false;this.areaList = response.data.data
    • 方式二:重写setdata方法,如下
      setData:function(obj){    
      let that = this;    
      let keys = [];    
      let val,data;    
      Object.keys(obj).forEach(function(key){    
       keys = key.split('.');    
       val = obj[key];    
       data = that.$data;    
       keys.forEach(function(key2,index){    
           if(index+1 == keys.length){    
               that.$set(data,key2,val);    
           }else{    
               if(!data[key2]){    
                  that.$set(data,key2,{});    
               }    
           }    
           data = data[key2];    
       })    
      });    
      }  

      以上代码出处:https://ask.dcloud.net.cn/article/35020

  6. 把之前页面的wxml内容复制到vue文件的template下的view下,然后执行如下改动
    • 属性绑定从
      attr="{{ a }}",改为 :attr="a"
      title="复选框{{ item }}" 改为 :title="'复选框' + item"
    • 事件绑定从 bind:click="toggleActionSheet1" 改为 @click="toggleActionSheet1"
    • 阻止事件冒泡 从 catch:tap="xx" 改为 @tap.native.stop="xx"
    • wx:if 改为 v-if
    • wx:for="{{ list }}" wx:key="{{ index }}" 改为`v-for="(item,index) in list"
  7. 微信小程序自定义组件处理
    之前引入的自定义组件,需要放到wxcomponents下,并在pages.json里注册。如果这里有js,并且被其他代码引入,要注意修改引用代码的路径指向。如下:
    {  
    "pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages  
        {"path": "pages/dashboard/dashboard"},  
        {  
            "path": "pages/action-sheet/action-sheet",  
            "style":{  
                "navigationBarTitleText":"ActionSheet 上拉菜单",  
                "usingComponents":{ //这里单页面引入action-sheet组件  
                    "van-action-sheet": "/wxcomponents/vant/action-sheet/index"  
                }  
            }  
        }  
    ],  
    "globalStyle": {  
        "navigationBarTitleText": "Vant For Uni-app",  
        "usingComponents": { //这里给所有页面全局引入了如下组件  
            "demo-block": "/wxcomponents/vant/demo-block/index",  
            "van-button": "/wxcomponents/vant/button/index",  
            "van-cell": "/wxcomponents/vant/cell/index",  
            "van-cell-group": "/wxcomponents/vant/cell-group/index",  
            "van-icon": "/wxcomponents/vant/icon/index",  
            "van-loading": "/wxcomponents/vant/loading/index",  
            "van-toast": "/wxcomponents/vant/toast/index"  
        }  
    }  
    }

    微信自定义组件虽然可以这样转换。但转换后只能用于微信和App。如果想用于支付宝百度头条,则需要新建swancomponents等目录,将微信自定义组件复制到这些目录,改造测试。虽然各小程序平台均支持自定义组件,但细节有差异,仍需自己测试。无论如何,H5端不支持这些自定义组件。
    比较妥善的跨端做法,是在uni-app插件市场寻找类似功能的vue组件,废弃之前的小程序自定义组件。比如把wx-charts换成ucharts、把wx-parser换成uparser。

辅助工具

贡献几个替换用的正则

str = str.replace(/bindtap/g, '@onclick');  
str = str.replace(/wx:if/g, 'v-show');  
str = str.replace(/src=\'\{\{/g, ":src='");  
str = str.replace(/wx\:key=\"\*this\"/g, ' ');  
str = str.replace(/wx\:key\=\"index\"/g, ' ');  
str = str.replace(/wx:for="{{/g, v-for= "(item,index) in ');  
str = str.replace(/bindinput/g, '@input'); 

wx.是否要替换为uni.

关于js api中的wx.,不要全局替换为uni.。因为有的wx的api是微信独有的,替换为uni后,反而在微信下没法用了。

同时uni-app编译器提供了把wx.编译为不同平台的机制,所以直接使用wx.的api完全可以正常在各端运行。

所以对于老代码,替不替换不重要,不影响运行,只影响语法提示和转到定义。

但是新写的代码,还是要用uni.的api,在代码提示、转到定义方面更强大。

App端迁移,还需处理服务端相关代码

如果把微信小程序转换为uni-app,仍然用于发布微信小程序,那服务器端代码不变。

但如果要发布到App、其他小程序等平台,服务器也需要调整部分代码。比如登陆、支付、推送、定位、地图等联网服务。

uni-app在客户端侧提供了统一的代码,比如uni.login、uni.requestPayment,在不同端均可以实现登陆、支付。

但服务器端的接口不一样,比如微信的App支付和小程序支付的申请开通、服务器接口都不一样,所以配置和服务器接口仍需单独处理。

比如把小程序转换uni-app后,需要打包发布为app,则需要向微信申请app支付的资质,拿到appkey等信息,填写到uni-app工程的 manifest-app -> sdk配置 -> 微信支付 下面,然后打包才能成效(如果是离线打包,参考离线打包的文件)。同时服务器需要按照微信的App支付的接口再开发对接。

同样微信小程序里内置的定位、地图,在app上,需要单独向高德等三方服务商申请,否则无法在app里使用这些服务。

这些sdk申请方式在 manifest -> app sdk配置 下有教程链接。

其他注意

参考:这里有一个转换示例,把vant weapp的小程序演示demo,转换为uni-app工程,里面的pages下同时保留了wxml和vue,可用于对比参考。http://ext.dcloud.net.cn/plugin?id=302

如果是mpvue的小程序,转换uni-app很方面,参考文档:https://ask.dcloud.net.cn/article/34945

感谢zhangdaren开源了小程序到uni-app的转换器

继续阅读 »

号外:推荐使用HBuilderX插件https://ext.dcloud.net.cn/plugin?id=2656,直接转换小程序到uni-app

本文首先分享转换步骤和原理,文末会分享三方开发者制作的 小程序转uni-app的转换器wepy转uni-app转换器

小程序转换uni-app的步骤

微信小程序的语法,其实是vue.js语法的裁剪定制版,在数据绑定、自定义组件等很多方面都有相似之处。
以下是一个小程序源码转换步骤指南:

客户端代码转换

  1. 新建一个uni-app项目,把之前的app.js、app.wxss的代码,挪到app.vue里,分别放到script和style节点下面
    如果其中有globalData等全局变量或方法,也直接放到app.vue的script下
    export default {    
        globalData: {    
            text: 'text'    
        },    
        onLaunch: function() {    
            console.log('App Launch')    
        }  
    }  

    读取globalData或赋值的方法是getApp().globalData.text = 'test'

  2. 转换app.json为pages.json,把每个小程序page目录下的index.json(或页面名称对应的json)里的配置取出来,放到pages.json的style下
  3. pages下每个页面目录放一个vue空文件模板
  4. 把之前页面的wxss内容复制到vue文件的style中,无需改动
  5. 把之前页面的js内容复制到vue文件的script中,然后执行如下改动
    • 5.1 之前js里面的data,放到新的data return下
      之前
      Page({  
      data: {  
      show1: false  
      }  
      })

      现在

      <script>  
      export default {  
      data() {  
          return {  
              show1: false  
          }  
      }  
      }  
      </script>
    • 5.2 之前js里面的自定义方法,放到新的method下
      之前
      Page({  
      toggleActionSheet1() {  
      this.setData({show1: true});  
      }  
      })

      现在

      <script>  
      export default {  
      methods: {  
          toggleActionSheet1() {  
              this.show1 = true  
          }  
      }  
      }  
      </script>
    • 5.3 之前js里面的生命周期函数onLoad、onShow等,直接放到export default下
      之前
      Page({  
      onLoad() {  
      console.log("page load");  
      }  
      })

      现在

      <script>  
      export default {  
      onLoad() {  
          console.log("page load");  
      }  
      }  
      </script>
    • 5.4 setdata的处理方式
    • 方式一:从 this.setData({loading: false,areaList: response.data.data}) 改为 this.loading = false;this.areaList = response.data.data
    • 方式二:重写setdata方法,如下
      setData:function(obj){    
      let that = this;    
      let keys = [];    
      let val,data;    
      Object.keys(obj).forEach(function(key){    
       keys = key.split('.');    
       val = obj[key];    
       data = that.$data;    
       keys.forEach(function(key2,index){    
           if(index+1 == keys.length){    
               that.$set(data,key2,val);    
           }else{    
               if(!data[key2]){    
                  that.$set(data,key2,{});    
               }    
           }    
           data = data[key2];    
       })    
      });    
      }  

      以上代码出处:https://ask.dcloud.net.cn/article/35020

  6. 把之前页面的wxml内容复制到vue文件的template下的view下,然后执行如下改动
    • 属性绑定从
      attr="{{ a }}",改为 :attr="a"
      title="复选框{{ item }}" 改为 :title="'复选框' + item"
    • 事件绑定从 bind:click="toggleActionSheet1" 改为 @click="toggleActionSheet1"
    • 阻止事件冒泡 从 catch:tap="xx" 改为 @tap.native.stop="xx"
    • wx:if 改为 v-if
    • wx:for="{{ list }}" wx:key="{{ index }}" 改为`v-for="(item,index) in list"
  7. 微信小程序自定义组件处理
    之前引入的自定义组件,需要放到wxcomponents下,并在pages.json里注册。如果这里有js,并且被其他代码引入,要注意修改引用代码的路径指向。如下:
    {  
    "pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages  
        {"path": "pages/dashboard/dashboard"},  
        {  
            "path": "pages/action-sheet/action-sheet",  
            "style":{  
                "navigationBarTitleText":"ActionSheet 上拉菜单",  
                "usingComponents":{ //这里单页面引入action-sheet组件  
                    "van-action-sheet": "/wxcomponents/vant/action-sheet/index"  
                }  
            }  
        }  
    ],  
    "globalStyle": {  
        "navigationBarTitleText": "Vant For Uni-app",  
        "usingComponents": { //这里给所有页面全局引入了如下组件  
            "demo-block": "/wxcomponents/vant/demo-block/index",  
            "van-button": "/wxcomponents/vant/button/index",  
            "van-cell": "/wxcomponents/vant/cell/index",  
            "van-cell-group": "/wxcomponents/vant/cell-group/index",  
            "van-icon": "/wxcomponents/vant/icon/index",  
            "van-loading": "/wxcomponents/vant/loading/index",  
            "van-toast": "/wxcomponents/vant/toast/index"  
        }  
    }  
    }

    微信自定义组件虽然可以这样转换。但转换后只能用于微信和App。如果想用于支付宝百度头条,则需要新建swancomponents等目录,将微信自定义组件复制到这些目录,改造测试。虽然各小程序平台均支持自定义组件,但细节有差异,仍需自己测试。无论如何,H5端不支持这些自定义组件。
    比较妥善的跨端做法,是在uni-app插件市场寻找类似功能的vue组件,废弃之前的小程序自定义组件。比如把wx-charts换成ucharts、把wx-parser换成uparser。

辅助工具

贡献几个替换用的正则

str = str.replace(/bindtap/g, '@onclick');  
str = str.replace(/wx:if/g, 'v-show');  
str = str.replace(/src=\'\{\{/g, ":src='");  
str = str.replace(/wx\:key=\"\*this\"/g, ' ');  
str = str.replace(/wx\:key\=\"index\"/g, ' ');  
str = str.replace(/wx:for="{{/g, v-for= "(item,index) in ');  
str = str.replace(/bindinput/g, '@input'); 

wx.是否要替换为uni.

关于js api中的wx.,不要全局替换为uni.。因为有的wx的api是微信独有的,替换为uni后,反而在微信下没法用了。

同时uni-app编译器提供了把wx.编译为不同平台的机制,所以直接使用wx.的api完全可以正常在各端运行。

所以对于老代码,替不替换不重要,不影响运行,只影响语法提示和转到定义。

但是新写的代码,还是要用uni.的api,在代码提示、转到定义方面更强大。

App端迁移,还需处理服务端相关代码

如果把微信小程序转换为uni-app,仍然用于发布微信小程序,那服务器端代码不变。

但如果要发布到App、其他小程序等平台,服务器也需要调整部分代码。比如登陆、支付、推送、定位、地图等联网服务。

uni-app在客户端侧提供了统一的代码,比如uni.login、uni.requestPayment,在不同端均可以实现登陆、支付。

但服务器端的接口不一样,比如微信的App支付和小程序支付的申请开通、服务器接口都不一样,所以配置和服务器接口仍需单独处理。

比如把小程序转换uni-app后,需要打包发布为app,则需要向微信申请app支付的资质,拿到appkey等信息,填写到uni-app工程的 manifest-app -> sdk配置 -> 微信支付 下面,然后打包才能成效(如果是离线打包,参考离线打包的文件)。同时服务器需要按照微信的App支付的接口再开发对接。

同样微信小程序里内置的定位、地图,在app上,需要单独向高德等三方服务商申请,否则无法在app里使用这些服务。

这些sdk申请方式在 manifest -> app sdk配置 下有教程链接。

其他注意

参考:这里有一个转换示例,把vant weapp的小程序演示demo,转换为uni-app工程,里面的pages下同时保留了wxml和vue,可用于对比参考。http://ext.dcloud.net.cn/plugin?id=302

如果是mpvue的小程序,转换uni-app很方面,参考文档:https://ask.dcloud.net.cn/article/34945

感谢zhangdaren开源了小程序到uni-app的转换器

收起阅读 »

uniapp开发的小说阅读APP

接口采用追书神器的接口,目前已经完成80%内容,兄弟们只要楼层1000层免费分享源代码

因追书神器接口失效先更换其他免费接口

下载地址:https://ext.dcloud.net.cn/plugin?id=2284

80%(完成度)

继续阅读 »

接口采用追书神器的接口,目前已经完成80%内容,兄弟们只要楼层1000层免费分享源代码

因追书神器接口失效先更换其他免费接口

下载地址:https://ext.dcloud.net.cn/plugin?id=2284

80%(完成度)

收起阅读 »

全职 接MUI 5+ UNIAPP 项目外包

uniapp 外包

熟练 uniapp mui 5+ php ,有外包需求可以找我,QQ6864952

全职外包 全栈开发

前段时间出了下差,很多加我的朋友,说声抱歉,现在回来了,欢迎砸单

熟练 uniapp mui 5+ php ,有外包需求可以找我,QQ6864952

全职外包 全栈开发

前段时间出了下差,很多加我的朋友,说声抱歉,现在回来了,欢迎砸单

利用mui 自定义吸顶(完美兼容ios跟安卓)-下拉刷新

body{
padding: 0rem !important;
margin: 0rem !important;
background: white;
line-height: 0.42rem !important;
font-family: "黑体";
touch-action: none;
}

/头部自定义样式/

top{

display:flex;  
height:0.88rem;  
line-height:0.88rem;  
background: white;  
padding: 0px !important;  
margin: 0px !important;  

}

top>div:nth-of-type(1){

width:15%;  

}

top>div:nth-of-type(1) img{

width: 0.52rem;  
height: 0.52rem;  
margin-left: 0.32rem;  
position: absolute;  
bottom: 0.18rem;  

}

top>div:nth-of-type(2){

width:70%;  
text-align: center;  
font-size: 0.36rem;  
white-space: nowrap;  
text-overflow: ellipsis;  
overflow: hidden;  
color: #333333 !important;  
font-weight: bold;  
box-sizing: border-box;  

}

top>div:nth-of-type(3){

width:15%;  
padding-right: 0.24rem;  

}

top>div:nth-of-type(3) img{

float:right;  

}
/头部样式结束/

/中间内容/
/.mui-bar-nav~.mui-content {
padding-top: 0.88rem;
}
/

mui-scroll-wrapper{

top: 0.88rem;  
background: white !important;  

}

.boxByImage{
height: 7.18rem;
text-align: center;
background: white;
}
.boxByImage img{
width:100%;
height: 100%;
}

/吸顶tabbar选项卡/
.tabList{
width: 100%;
height:1rem;
display: flex;
border-bottom:1px solid #F0F0F0;
background: white;
}

tabList{

opacity: 0;     
height: 0rem;   
background: white;  

}
.tabList>div{
flex: 1;
height:1rem;
text-align: center;
line-height: 1rem;
font-size: 0.32rem;
font-weight:400;
color:rgba(51,51,51,1);
position: relative;
}
.tabList span{
width:0.32rem;
height:0.04rem;
background:#2CE27A;
position: absolute;
bottom: 0.16rem;
left:50%;
margin-left: -0.16rem;
animation: bbh 0.6s;
}

/间距条/
.jx{
height: 0.2rem;
background: #F7F7F7;
}

/下拉刷新的盒子---自定义的里面可以自己随便加什么都可以/
.upLoad{
width: 100%;
text-align: center;
font-size: 0.36rem;
height:1.28rem;
line-height:1.28rem;
margin-top: -1.28rem;
opacity: 0;
color: #CCCCCC;
}
.upLoad img{
width: 100%;
height: 100%;
}

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<title>根据mui-scroll-wrapper的滑动特性,完美兼容安卓ios吸顶效果,还可自定义下拉刷新</title>
<link href="css/mui.min.css" rel="stylesheet"/>
<link rel="stylesheet" href="css/index.css" />
<!--根据苹果7去适配,1rem=100px-->
<script>document.documentElement.style.fontSize = document.documentElement.clientWidth / 750 * 100 + 'px';</script>
</head>
<body>
<div id="top" class="mui-bar mui-bar-nav">
<div><img src="black.png" alt="" /></div>
<div><span>吸顶(兼容安卓跟ios)+下拉刷新</span></div>
<div></div>
</div>

<div class="mui-content">  
    <!--这里的布局不能放在mui-scroll-wrapper,不然固定定位失效-->  
    <div class="tabList" id="tabList">  
        <div>布局1</div>  
        <div>布局2</div>  
        <div>布局3</div>  
    </div>  

    <div class="mui-scroll-wrapper" id="mui-scroll-wrapper">  
        <div class="mui-scroll">  
            <!--下拉刷新布局-->  
            <div class="upLoad"><img src="down.gif" alt="" /></div>  

            <!--图片-->  
            <div class="boxByImage"><img src="home_top.png" alt="" /></div>  

            <!--间距-->  
            <div class="jx"></div>  

            <!--这个布局跟上面要一样,多增加了一个tabListInScroll属性-->  
            <div class="tabList tabListInScroll">  
                <div>布局1</div>  
                <div>布局2</div>  
                <div>布局3</div>  
            </div>  

            <!--增加一些数据模拟效果-->  
            <div>item1</div>  
            <div>item2</div>  
            <div>item3</div>  
            <div>item4</div>  
            <div>item5</div>  
            <div>item6</div>  
            <div>item7</div>  
            <div>item8</div>  
            <div>item9</div>  
            <div>item10</div>  
            <div>item11</div>  
            <div>item12</div>  
            <div>item13</div>  
            <div>item14</div>  
            <div>item15</div>  
            <div>item16</div>  
            <div>item17</div>  
            <div>item18</div>  
            <div>item19</div>  
            <div>item20</div>  
            <div>item21</div>  
            <div>item22</div>  
            <div>item23</div>  
            <div>item24</div>  
            <div>item25</div>  
            <div>item26</div>  
            <div>item27</div>  
            <div>item28</div>  
            <div>item29</div>  
            <div>item30</div>  
            <div>item31</div>  
            <div>item32</div>  
            <div>item33</div>  
            <div>item34</div>  
            <div>item35</div>  
            <div>item36</div>  
            <div>item37</div>  
            <div>item38</div>  
            <div>item39</div>  
            <div>item40</div>  
            <div>item41</div>  
            <div>item42</div>  
            <div>item43</div>  
            <div>item44</div>  
            <div>item45</div>  
            <div>item46</div>  
            <div>item47</div>  
            <div>item48</div>  
            <div>item49</div>  
            <div>item50</div>  
        </div>  
    </div>  
</div>  

</body>
<script src="js/jquery_3.0.js"></script>
<script src="js/mui.min.js"></script>
<script>
mui.init();
var boxImght = $(".boxByImage").height(); //中间图片的高度
var topHeight = $("#top").height(); //头部高度
var tabList = $(".tabList").height(); //选项卡高度
var jx = $(".jx").height(); //间隙高度
var scroll = mui('#mui-scroll-wrapper').scroll(); //scroll滑动的距离方法

    var obj  = {                                                //下拉刷新  
        upFlag : true,                                          //滑动开关,不然下拉刷新会一直闪烁因为滑动是时时的  
        time   : 800                                            //展示的时间  
    }  

    //头部动画效果   boxImght+jx就是向上滑的距离,然后就自动吸顶,这里通过控制opacity属性,让吸顶更自然,而不用通过定位方式去做(麻烦);  
    document.querySelector('#mui-scroll-wrapper').addEventListener('scroll', function (e ){   
        //吸顶效果  
        if(scroll.y<=-(boxImght+jx)){                                  
            $("#mui-scroll-wrapper").css("top","1.88rem");        //这里的1.88rem是css里面头部高度加上tabList选项卡的高度相加而来的;  
            $("#tabList").css({height : "1rem",opacity : "1"});  
            $(".tabListInScroll").hide();  
        }else{  
            $("#mui-scroll-wrapper").css("top","0.88rem");        //默认css就是0.88rem;  
            $("#tabList").css({height : "0rem",opacity : "0"});  
            $(".tabListInScroll").show();  
        }  
    })  

    //下拉刷新  
    document.addEventListener('touchmove',function(e){  
        if(obj.upFlag){  
            if(scroll.y>80){                                 //触发下拉的最小距离80可以自定义  
                obj.upFlag = false;  
                $(".upLoad").animate({                            //展示下拉刷新内容  
                    opacity : 1,  
                    "margin-top":"0rem"  
                });  
            };  
        };  
    });  
    document.addEventListener('touchend',function(e){  
        setTimeout(function(){  
            $(".upLoad").animate({                                //隐藏下拉刷新内容,500ms是动画 效果  
                opacity : 0,  
                "margin-top":"-1.28rem"                         //默认css展示的外边距去隐藏  
            });  
            mui.toast("我刷新了");  
            obj.upFlag = true;  
        },obj.time);  
    });  

</script>
</html>

用最简单的代码,实现最完美的功能,里面的mui.min.js,还要jquery自己cdn引入下就好了,没有其它js了;

继续阅读 »

body{
padding: 0rem !important;
margin: 0rem !important;
background: white;
line-height: 0.42rem !important;
font-family: "黑体";
touch-action: none;
}

/头部自定义样式/

top{

display:flex;  
height:0.88rem;  
line-height:0.88rem;  
background: white;  
padding: 0px !important;  
margin: 0px !important;  

}

top>div:nth-of-type(1){

width:15%;  

}

top>div:nth-of-type(1) img{

width: 0.52rem;  
height: 0.52rem;  
margin-left: 0.32rem;  
position: absolute;  
bottom: 0.18rem;  

}

top>div:nth-of-type(2){

width:70%;  
text-align: center;  
font-size: 0.36rem;  
white-space: nowrap;  
text-overflow: ellipsis;  
overflow: hidden;  
color: #333333 !important;  
font-weight: bold;  
box-sizing: border-box;  

}

top>div:nth-of-type(3){

width:15%;  
padding-right: 0.24rem;  

}

top>div:nth-of-type(3) img{

float:right;  

}
/头部样式结束/

/中间内容/
/.mui-bar-nav~.mui-content {
padding-top: 0.88rem;
}
/

mui-scroll-wrapper{

top: 0.88rem;  
background: white !important;  

}

.boxByImage{
height: 7.18rem;
text-align: center;
background: white;
}
.boxByImage img{
width:100%;
height: 100%;
}

/吸顶tabbar选项卡/
.tabList{
width: 100%;
height:1rem;
display: flex;
border-bottom:1px solid #F0F0F0;
background: white;
}

tabList{

opacity: 0;     
height: 0rem;   
background: white;  

}
.tabList>div{
flex: 1;
height:1rem;
text-align: center;
line-height: 1rem;
font-size: 0.32rem;
font-weight:400;
color:rgba(51,51,51,1);
position: relative;
}
.tabList span{
width:0.32rem;
height:0.04rem;
background:#2CE27A;
position: absolute;
bottom: 0.16rem;
left:50%;
margin-left: -0.16rem;
animation: bbh 0.6s;
}

/间距条/
.jx{
height: 0.2rem;
background: #F7F7F7;
}

/下拉刷新的盒子---自定义的里面可以自己随便加什么都可以/
.upLoad{
width: 100%;
text-align: center;
font-size: 0.36rem;
height:1.28rem;
line-height:1.28rem;
margin-top: -1.28rem;
opacity: 0;
color: #CCCCCC;
}
.upLoad img{
width: 100%;
height: 100%;
}

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<title>根据mui-scroll-wrapper的滑动特性,完美兼容安卓ios吸顶效果,还可自定义下拉刷新</title>
<link href="css/mui.min.css" rel="stylesheet"/>
<link rel="stylesheet" href="css/index.css" />
<!--根据苹果7去适配,1rem=100px-->
<script>document.documentElement.style.fontSize = document.documentElement.clientWidth / 750 * 100 + 'px';</script>
</head>
<body>
<div id="top" class="mui-bar mui-bar-nav">
<div><img src="black.png" alt="" /></div>
<div><span>吸顶(兼容安卓跟ios)+下拉刷新</span></div>
<div></div>
</div>

<div class="mui-content">  
    <!--这里的布局不能放在mui-scroll-wrapper,不然固定定位失效-->  
    <div class="tabList" id="tabList">  
        <div>布局1</div>  
        <div>布局2</div>  
        <div>布局3</div>  
    </div>  

    <div class="mui-scroll-wrapper" id="mui-scroll-wrapper">  
        <div class="mui-scroll">  
            <!--下拉刷新布局-->  
            <div class="upLoad"><img src="down.gif" alt="" /></div>  

            <!--图片-->  
            <div class="boxByImage"><img src="home_top.png" alt="" /></div>  

            <!--间距-->  
            <div class="jx"></div>  

            <!--这个布局跟上面要一样,多增加了一个tabListInScroll属性-->  
            <div class="tabList tabListInScroll">  
                <div>布局1</div>  
                <div>布局2</div>  
                <div>布局3</div>  
            </div>  

            <!--增加一些数据模拟效果-->  
            <div>item1</div>  
            <div>item2</div>  
            <div>item3</div>  
            <div>item4</div>  
            <div>item5</div>  
            <div>item6</div>  
            <div>item7</div>  
            <div>item8</div>  
            <div>item9</div>  
            <div>item10</div>  
            <div>item11</div>  
            <div>item12</div>  
            <div>item13</div>  
            <div>item14</div>  
            <div>item15</div>  
            <div>item16</div>  
            <div>item17</div>  
            <div>item18</div>  
            <div>item19</div>  
            <div>item20</div>  
            <div>item21</div>  
            <div>item22</div>  
            <div>item23</div>  
            <div>item24</div>  
            <div>item25</div>  
            <div>item26</div>  
            <div>item27</div>  
            <div>item28</div>  
            <div>item29</div>  
            <div>item30</div>  
            <div>item31</div>  
            <div>item32</div>  
            <div>item33</div>  
            <div>item34</div>  
            <div>item35</div>  
            <div>item36</div>  
            <div>item37</div>  
            <div>item38</div>  
            <div>item39</div>  
            <div>item40</div>  
            <div>item41</div>  
            <div>item42</div>  
            <div>item43</div>  
            <div>item44</div>  
            <div>item45</div>  
            <div>item46</div>  
            <div>item47</div>  
            <div>item48</div>  
            <div>item49</div>  
            <div>item50</div>  
        </div>  
    </div>  
</div>  

</body>
<script src="js/jquery_3.0.js"></script>
<script src="js/mui.min.js"></script>
<script>
mui.init();
var boxImght = $(".boxByImage").height(); //中间图片的高度
var topHeight = $("#top").height(); //头部高度
var tabList = $(".tabList").height(); //选项卡高度
var jx = $(".jx").height(); //间隙高度
var scroll = mui('#mui-scroll-wrapper').scroll(); //scroll滑动的距离方法

    var obj  = {                                                //下拉刷新  
        upFlag : true,                                          //滑动开关,不然下拉刷新会一直闪烁因为滑动是时时的  
        time   : 800                                            //展示的时间  
    }  

    //头部动画效果   boxImght+jx就是向上滑的距离,然后就自动吸顶,这里通过控制opacity属性,让吸顶更自然,而不用通过定位方式去做(麻烦);  
    document.querySelector('#mui-scroll-wrapper').addEventListener('scroll', function (e ){   
        //吸顶效果  
        if(scroll.y<=-(boxImght+jx)){                                  
            $("#mui-scroll-wrapper").css("top","1.88rem");        //这里的1.88rem是css里面头部高度加上tabList选项卡的高度相加而来的;  
            $("#tabList").css({height : "1rem",opacity : "1"});  
            $(".tabListInScroll").hide();  
        }else{  
            $("#mui-scroll-wrapper").css("top","0.88rem");        //默认css就是0.88rem;  
            $("#tabList").css({height : "0rem",opacity : "0"});  
            $(".tabListInScroll").show();  
        }  
    })  

    //下拉刷新  
    document.addEventListener('touchmove',function(e){  
        if(obj.upFlag){  
            if(scroll.y>80){                                 //触发下拉的最小距离80可以自定义  
                obj.upFlag = false;  
                $(".upLoad").animate({                            //展示下拉刷新内容  
                    opacity : 1,  
                    "margin-top":"0rem"  
                });  
            };  
        };  
    });  
    document.addEventListener('touchend',function(e){  
        setTimeout(function(){  
            $(".upLoad").animate({                                //隐藏下拉刷新内容,500ms是动画 效果  
                opacity : 0,  
                "margin-top":"-1.28rem"                         //默认css展示的外边距去隐藏  
            });  
            mui.toast("我刷新了");  
            obj.upFlag = true;  
        },obj.time);  
    });  

</script>
</html>

用最简单的代码,实现最完美的功能,里面的mui.min.js,还要jquery自己cdn引入下就好了,没有其它js了;

收起阅读 »

Android平台签名证书(.keystore)生成指南

Android证书

Android平台打包发布apk应用,需要使用数字证书(.keystore文件)进行签名,用于表明开发者身份。

Android证书的生成是自助和免费的,不需要审批或付费。

可以使用JRE环境中的keytool命令生成。以下是windows平台生成证书的方法:

安装JRE环境(推荐使用JRE8环境,如已有可跳过)

可从Oracle官方下载jre安装包:https://www.oracle.com/java/technologies/downloads/#java8
下面以Windows平台,jre安装目录为“C:\Program Files\Java\jre1.8.0_201”为例,实际操作时请修改为自己安装目录
建议将JRE安装路径添加到系统环境变量,已配置可跳过此章节
打开命令行(cmd),输入以下命令:

d:  
set PATH=%PATH%;"C:\Program Files\Java\jre1.8.0_201\bin"
  • 第一行:切换工作目录到D:路径
  • 第二行:将jre命令添加到临时环境变量中

生成签名证书

使用keytool -genkey命令生成证书:

keytool -genkey -alias testalias -keyalg RSA -keysize 2048 -validity 36500 -keystore test.keystore
  • testalias是证书别名,可修改为自己想设置的字符,建议使用英文字母和数字
  • test.keystore是证书文件名称,可修改为自己想设置的文件名称,也可以指定完整文件路径
  • 36500是证书的有效期,表示100年有效期,单位天,建议时间设置长一点,避免证书过期

回车后会提示:

Enter keystore password:  //输入证书文件密码,输入完成回车  
Re-enter new password:   //再次输入证书文件密码,输入完成回车  
What is your first and last name?  
  [Unknown]:  //输入名字和姓氏,输入完成回车  
What is the name of your organizational unit?  
  [Unknown]:  //输入组织单位名称,输入完成回车  
What is the name of your organization?  
  [Unknown]:  //输入组织名称,输入完成回车  
What is the name of your City or Locality?  
  [Unknown]:  //输入城市或区域名称,输入完成回车  
What is the name of your State or Province?  
  [Unknown]:  //输入省/市/自治区名称,输入完成回车  
What is the two-letter country code for this unit?  
  [Unknown]:  //输入国家/地区代号(两个字母),中国为CN,输入完成回车  
Is CN=XX, OU=XX, O=XX, L=XX, ST=XX, C=XX correct?  
  [no]:  //确认上面输入的内容是否正确,输入y,回车  

Enter key password for <testalias>  
        (RETURN if same as keystore password):  //确认证书密码与证书文件密码一样(HBuilder|HBuilderX要求这两个密码一致),直接回车就可以

以上命令运行完成后就会生成证书,路径为“D:\test.keystore”。

注意:上述信息填写要规范,乱填有可能会影响应用上架应用市场。

<a id="keyinfo"/>

查看证书信息

可以使用以下命令查看:

keytool -list -v -keystore test.keystore  
Enter keystore password: //输入密码,回车

会输出以下格式信息:

Keystore type: PKCS12    
Keystore provider: SUN    

Your keystore contains 1 entry    

Alias name: test    
Creation date: 2019-10-28    
Entry type: PrivateKeyEntry    
Certificate chain length: 1    
Certificate[1]:    
Owner: CN=Tester, OU=Test, O=Test, L=HD, ST=BJ, C=CN    
Issuer: CN=Tester, OU=Test, O=Test, L=HD, ST=BJ, C=CN    
Serial number: 7dd12840    
Valid from: Fri Jul 26 20:52:56 CST 2019 until: Sun Jul 02 20:52:56 CST 2119    
Certificate fingerprints:    
         MD5:  F9:F6:C8:1F:DB:AB:50:14:7D:6F:2C:4F:CE:E6:0A:A5    
         SHA1: BB:AC:E2:2F:97:3B:18:02:E7:D6:69:A3:7A:28:EF:D2:3F:A3:68:E7    
         SHA256: 24:11:7D:E7:36:12:BC:FE:AF:2A:6A:24:BD:04:4F:2E:33:E5:2D:41:96:5F:50:4D:74:17:7F:4F:E2:55:EB:26    
Signature algorithm name: SHA256withRSA    
Subject Public Key Algorithm: 2048-bit RSA key    
Version: 3

其中证书指纹信息(Certificate fingerprints):

  • MD5
    证书的MD5指纹信息(安全码MD5)
  • SHA1
    证书的SHA1指纹信息(安全码SHA1)
  • SHA256
    证书的SHA256指纹信息(安全码SHA245)

安卓签名获取工具

直接通过一个apk,获取安装到手机的第三方应用签名的apk包。 详情:https://developers.weixin.qq.com/doc/oplatform/Downloads/Android_Resource.html

注意事项

云端打包默认会添加V1/V2签名,已知V1签名不支持2048位的DSA算法,使用2048-bit DSA key云端打包可能失败,提示以下错误:

* What went wrong:  
Execution failed for task ':app:packageRelease'.  
> A failure occurred while executing com.android.build.gradle.tasks.PackageAndroidArtifact$IncrementalSplitterRunnable  
   > java.io.IOException: Failed to generate v1 signature

解决方法

  • 第一种方法:重新生成证书,在生成证书命令中添加“-keyalg RSA”参数指定使用RSA算法
  • 第二种方法:设置miniSdkVersion大于等于24,因为V2签名需Android7及以上设备才支持,设置miniSdkVersion大于等于24表示不支持android7以下设备,从而不需要包含V1签名,设置miniSdkVersion详情参考:https://ask.dcloud.net.cn/article/193

查看证书算法的方法
使用“keytool -list -v”查看证书信息,看“Subject Public Key Algorithm: ”项的信息,如下表示使用DSA算法:

Subject Public Key Algorithm: 2048-bit DSA key

其他

可获取MD5签名的JDK版本分享

【详见】https://ask.dcloud.net.cn/article/38778

继续阅读 »

Android平台打包发布apk应用,需要使用数字证书(.keystore文件)进行签名,用于表明开发者身份。

Android证书的生成是自助和免费的,不需要审批或付费。

可以使用JRE环境中的keytool命令生成。以下是windows平台生成证书的方法:

安装JRE环境(推荐使用JRE8环境,如已有可跳过)

可从Oracle官方下载jre安装包:https://www.oracle.com/java/technologies/downloads/#java8
下面以Windows平台,jre安装目录为“C:\Program Files\Java\jre1.8.0_201”为例,实际操作时请修改为自己安装目录
建议将JRE安装路径添加到系统环境变量,已配置可跳过此章节
打开命令行(cmd),输入以下命令:

d:  
set PATH=%PATH%;"C:\Program Files\Java\jre1.8.0_201\bin"
  • 第一行:切换工作目录到D:路径
  • 第二行:将jre命令添加到临时环境变量中

生成签名证书

使用keytool -genkey命令生成证书:

keytool -genkey -alias testalias -keyalg RSA -keysize 2048 -validity 36500 -keystore test.keystore
  • testalias是证书别名,可修改为自己想设置的字符,建议使用英文字母和数字
  • test.keystore是证书文件名称,可修改为自己想设置的文件名称,也可以指定完整文件路径
  • 36500是证书的有效期,表示100年有效期,单位天,建议时间设置长一点,避免证书过期

回车后会提示:

Enter keystore password:  //输入证书文件密码,输入完成回车  
Re-enter new password:   //再次输入证书文件密码,输入完成回车  
What is your first and last name?  
  [Unknown]:  //输入名字和姓氏,输入完成回车  
What is the name of your organizational unit?  
  [Unknown]:  //输入组织单位名称,输入完成回车  
What is the name of your organization?  
  [Unknown]:  //输入组织名称,输入完成回车  
What is the name of your City or Locality?  
  [Unknown]:  //输入城市或区域名称,输入完成回车  
What is the name of your State or Province?  
  [Unknown]:  //输入省/市/自治区名称,输入完成回车  
What is the two-letter country code for this unit?  
  [Unknown]:  //输入国家/地区代号(两个字母),中国为CN,输入完成回车  
Is CN=XX, OU=XX, O=XX, L=XX, ST=XX, C=XX correct?  
  [no]:  //确认上面输入的内容是否正确,输入y,回车  

Enter key password for <testalias>  
        (RETURN if same as keystore password):  //确认证书密码与证书文件密码一样(HBuilder|HBuilderX要求这两个密码一致),直接回车就可以

以上命令运行完成后就会生成证书,路径为“D:\test.keystore”。

注意:上述信息填写要规范,乱填有可能会影响应用上架应用市场。

<a id="keyinfo"/>

查看证书信息

可以使用以下命令查看:

keytool -list -v -keystore test.keystore  
Enter keystore password: //输入密码,回车

会输出以下格式信息:

Keystore type: PKCS12    
Keystore provider: SUN    

Your keystore contains 1 entry    

Alias name: test    
Creation date: 2019-10-28    
Entry type: PrivateKeyEntry    
Certificate chain length: 1    
Certificate[1]:    
Owner: CN=Tester, OU=Test, O=Test, L=HD, ST=BJ, C=CN    
Issuer: CN=Tester, OU=Test, O=Test, L=HD, ST=BJ, C=CN    
Serial number: 7dd12840    
Valid from: Fri Jul 26 20:52:56 CST 2019 until: Sun Jul 02 20:52:56 CST 2119    
Certificate fingerprints:    
         MD5:  F9:F6:C8:1F:DB:AB:50:14:7D:6F:2C:4F:CE:E6:0A:A5    
         SHA1: BB:AC:E2:2F:97:3B:18:02:E7:D6:69:A3:7A:28:EF:D2:3F:A3:68:E7    
         SHA256: 24:11:7D:E7:36:12:BC:FE:AF:2A:6A:24:BD:04:4F:2E:33:E5:2D:41:96:5F:50:4D:74:17:7F:4F:E2:55:EB:26    
Signature algorithm name: SHA256withRSA    
Subject Public Key Algorithm: 2048-bit RSA key    
Version: 3

其中证书指纹信息(Certificate fingerprints):

  • MD5
    证书的MD5指纹信息(安全码MD5)
  • SHA1
    证书的SHA1指纹信息(安全码SHA1)
  • SHA256
    证书的SHA256指纹信息(安全码SHA245)

安卓签名获取工具

直接通过一个apk,获取安装到手机的第三方应用签名的apk包。 详情:https://developers.weixin.qq.com/doc/oplatform/Downloads/Android_Resource.html

注意事项

云端打包默认会添加V1/V2签名,已知V1签名不支持2048位的DSA算法,使用2048-bit DSA key云端打包可能失败,提示以下错误:

* What went wrong:  
Execution failed for task ':app:packageRelease'.  
> A failure occurred while executing com.android.build.gradle.tasks.PackageAndroidArtifact$IncrementalSplitterRunnable  
   > java.io.IOException: Failed to generate v1 signature

解决方法

  • 第一种方法:重新生成证书,在生成证书命令中添加“-keyalg RSA”参数指定使用RSA算法
  • 第二种方法:设置miniSdkVersion大于等于24,因为V2签名需Android7及以上设备才支持,设置miniSdkVersion大于等于24表示不支持android7以下设备,从而不需要包含V1签名,设置miniSdkVersion详情参考:https://ask.dcloud.net.cn/article/193

查看证书算法的方法
使用“keytool -list -v”查看证书信息,看“Subject Public Key Algorithm: ”项的信息,如下表示使用DSA算法:

Subject Public Key Algorithm: 2048-bit DSA key

其他

可获取MD5签名的JDK版本分享

【详见】https://ask.dcloud.net.cn/article/38778

收起阅读 »

HBuilderX自定义UI主题界面风格

主题 界面风格

自定义UI主题介绍

HBuilderX的窗体所有颜色都在配置文件中,并且可以复写以实现个性化。从HBuilderX 1.8.8起,支持自定义配置。

在HBuilderX的设置文件setting.json的源码视图中,通过定义"workbench.colorCustomizations"字段来自定义您喜欢的主题颜色。

从HBuilderX 2.9.2起,还支持使用插件直接配置主题,避免自己写复杂的配置,这个插件包括了很多常见主题:https://ext.dcloud.net.cn/plugin?id=2684

常用配置

  • 目前只能在现有的3个主题Default柔和、Monokai酷黑、Atom One Dark雅蓝的基础之上复写,不能完全更新新主题。
  • 目前只能修改软件主题外观。代码区的颜色只有自带的三种风格。

关于左侧项目管理器的背景色,和右侧代码区的背景色,两者是统一还是有区别?不同人有不同习惯。比如一些喜欢酷黑主题的开发者希望左边项目管理器也变成纯黑。

在HBuilderX的默认主题中,绿柔是左右统一,雅蓝左右不统一,酷黑在HBuilderX1.9.8以前左右不统一。

下面是几种常见配置,可根据自己需求统一或差异化颜色。

"workbench.colorCustomizations": {  
    "[Default]": {//绿柔  
        "sideBar.background":"#faf6e6" //加深项目管理器背景色  
        //以下为老HBuilder的标准主题颜色  
        //"sideBar.background":"#FFFFCC",  
        //"editorGroupHeader.tabsBackground":"#FFFFCC",  
        //"tab.inactiveBackground":"#FFFFCC",  
        //"editor.background":"#FFFFCC" //取消设置需重启生效  
    },  
    "[Monokai]": {//酷黑  
        "toolBar.background": "#272822", //工具栏背景色设为黑色  
        //"sideBar.background":"#535353",//项目管理器浅色背景  
        "sideBar.background":"#272822" //项目管理器背景色设为黑色  
    },  
    "[Atom One Dark]": {//雅蓝  
        "sideBar.background": "#282c34" //项目管理器背景色设为与代码区背景色相同  
    }  
}

以下是几种常用配置的预览图:

  • 左右统一的黑色主题

  • 左侧颜色更深的绿柔

  • 老HBuilder默认的深黄色主题

以下为分类的颜色明细说明:

颜色配置命名规则与vscode相同,方便从vscode提取外观主题。

文本编辑区

名称 描述
editor.foreground 编辑区前景颜色
editor.background 编辑区背景颜色

注意:本主题自定义是外观方面,不同代码的着色自定义未开放。

mac标题栏(只在mac os上生效)

名称 描述
titleBar.activeBackground 背景颜色
titleBar.activeForeground 前景颜色

工具栏

名称 描述
toolBar.background 工具栏背景颜色
toolBar.border 工具栏边框颜色
toolBar.hoverBackground 工具栏上图标被选中时的背景颜色

文件资源管理器

名称 描述
sideBar.background 背景颜色
sideBar.border 边框颜色

标签卡

名称 描述
editorGroupHeader.tabsBackground tabs背景颜色
tab.activeBackground 选中时的背景颜色
tab.border 边框颜色
tab.inactiveBackground 未选中时的背景颜色
tab.hoverBackground 鼠标滑过时的背景颜色
tab.inactiveForeground 未选中时的前景颜色
tab.activeBorder 选中时的边框颜色
tab.activeForeground 选中时的前景颜色
tab.unfocusedInactiveForeground 未选中分栏里未选中标签的前景颜色
tab.unfocusedHoverBackground 未选中分栏里鼠标滑过未选中标签的背景颜色
tab.unfocusedActiveForeground 未选中分栏里选中标签的前景颜色

分栏

名称 描述
editorGroup.border 分割线颜色
tab.unfocusedActiveForeground 未激活分组里选中标签前景色

按钮

名称 描述
button.background 按钮背景颜色
button.foreground 按钮前景颜色
button.hoverBackground 鼠标滑过按钮背景颜色

代码助手

名称 描述
editorSuggestWidget.background 助手弹窗背景颜色
editorSuggestWidget.border 助手弹窗边框颜色
editorSuggestWidget.selectedBackground 助手弹窗选中条目时背景颜色
editorSuggestWidget.link 助手链接颜色

文本框

名称 描述
input.background 文本框背景颜色
input.foreground 文本框前景颜色
input.border 文本框边框颜色
focusBorder 文本框有焦点时边框颜色

列表控件/树控件区

名称 描述
list.foreground 前景颜色
list.activeSelectionBackground 选中条目背景颜色
list.activeSelectionForeground 选中条目前景颜色
list.hoverBackground 鼠标滑过背景颜色
list.highlightForeground 高亮时前景颜色
list.inactiveSelectionBackground 未选中条目背景颜色
list.inactiveSelectionForeground 未选中条目前景颜色
explorer.file.status.modified svn/git项目修改状态前景色
explorer.file.status.untracked svn/git项目未跟踪状态前景色
explorer.file.status.added svn/git项目添加状态前景色
explorer.file.status.conflicted svn/git项目冲突状态前景色
explorer.file.status.rename svn/git项目重命名状态前景色
explorer.file.status.marktext svn/git项目标记前景色

文档结构图

名称 描述
outlineBackground 文档结构背景颜色

滚动条

名称 描述
scrollbarSlider.background 滚动条背景颜色
scrollbarSlider.hoverBackground 鼠标滑过滚动条背景颜色

预览按钮

名称 描述
extensionButton.prominentBackground 背景颜色
extensionButton.prominentForeground 前景颜色
extensionButton.border 边框颜色
extensionButton.prominentHoverBackground 鼠标滑过时的背景颜色
extensionButton.checkColor 选中时的前景颜色

搜索下拉框

名称 描述
inputValidation.infoBackground 下拉框背景颜色
inputList.hoverBackground 鼠标滑过item背景颜色
inputList.border 下拉框边框颜色
inputList.titleColor 下拉框左边文字颜色
inputList.foreground 下拉框右边文字颜色

设置

名称 描述
editor.background 设置页面背景颜色
inputOption.activeBorder 文本框有焦点时边框颜色
settings.textInputBackground 文本框背景颜色
settings.textInputBorder 文本框边框颜色
settings.textInputDisableBackground 文本框不可用背景颜色
settings.dropdownForeground combobox下拉列表前景颜色
settings.dropdownBorder combobox下拉列表边框颜色
settings.dropdownBackground combobox下拉列表背景颜色
settings.dropdownListBorder combobox item边框颜色

图片预览

名称 描述
imageview.background 浅色方格颜色
imageview.foreground 深色方格颜色

弹窗提示框

名称 描述
notifications.border 弹窗边框颜色
notifications.background 弹窗背景颜色
notifications.foreground 弹窗前景颜色
notification.buttonBorder 弹窗按钮边框颜色
notification.buttonBackground 弹窗按钮背景颜色
notification.buttonForeground 弹窗按钮前景颜色
notification.buttonPressedForeground 弹窗按钮按下前景颜色
notification.buttonPressedBackground 弹窗按钮按下背景颜色
notificationLink.foreground 弹窗链接颜色

文件对比

名称 描述
filediff.line.add 添加行背景颜色
filediff.line.delete 删除行背景颜色
filediff.inline.base 行内比较和右边行有不同字符,左边字符显示颜色
filediff.inline.modify 行内比较和左边行有不同字符,右边字符显示颜色

控制台

名称 描述
terminal.background 终端背景颜色
console.background 控制台背景颜色
panelTitle.activeForeground tab选中时的前景颜色
debug.foreground 前景颜色

查看svn/git日志

在svn/git项目上点击右键。只在mac上生效

名称 描述
logviewButton.background 按钮背景颜色
logviewButton.border 按钮边框颜色
logviewButton.hover 鼠标滑过按钮背景颜色
logviewButton.disable 按钮不可用背景颜色
logviewButtonDisable.border 按钮不可用边框颜色
logview.file.action.modified 选中已修改文件前景色
logview.file.action.deleted 选中已删除文件前景色
logview.file.action.added 选中已增加文件前景色
logview.file.action.rename 选中已重命名文件前景色

状态栏

名称 描述
statusBar.background 状态栏背景颜色
statusBar.foreground 状态栏前景颜色

右侧迷你地图

名称 描述
minimap.handle.background 迷你地图滑块背景

开发者自定义的主题汇总

欢迎更多开发者提交自己配置的主题并共享出来。

主题插件

HBuilderX自定义主题辅助工具

继续阅读 »

自定义UI主题介绍

HBuilderX的窗体所有颜色都在配置文件中,并且可以复写以实现个性化。从HBuilderX 1.8.8起,支持自定义配置。

在HBuilderX的设置文件setting.json的源码视图中,通过定义"workbench.colorCustomizations"字段来自定义您喜欢的主题颜色。

从HBuilderX 2.9.2起,还支持使用插件直接配置主题,避免自己写复杂的配置,这个插件包括了很多常见主题:https://ext.dcloud.net.cn/plugin?id=2684

常用配置

  • 目前只能在现有的3个主题Default柔和、Monokai酷黑、Atom One Dark雅蓝的基础之上复写,不能完全更新新主题。
  • 目前只能修改软件主题外观。代码区的颜色只有自带的三种风格。

关于左侧项目管理器的背景色,和右侧代码区的背景色,两者是统一还是有区别?不同人有不同习惯。比如一些喜欢酷黑主题的开发者希望左边项目管理器也变成纯黑。

在HBuilderX的默认主题中,绿柔是左右统一,雅蓝左右不统一,酷黑在HBuilderX1.9.8以前左右不统一。

下面是几种常见配置,可根据自己需求统一或差异化颜色。

"workbench.colorCustomizations": {  
    "[Default]": {//绿柔  
        "sideBar.background":"#faf6e6" //加深项目管理器背景色  
        //以下为老HBuilder的标准主题颜色  
        //"sideBar.background":"#FFFFCC",  
        //"editorGroupHeader.tabsBackground":"#FFFFCC",  
        //"tab.inactiveBackground":"#FFFFCC",  
        //"editor.background":"#FFFFCC" //取消设置需重启生效  
    },  
    "[Monokai]": {//酷黑  
        "toolBar.background": "#272822", //工具栏背景色设为黑色  
        //"sideBar.background":"#535353",//项目管理器浅色背景  
        "sideBar.background":"#272822" //项目管理器背景色设为黑色  
    },  
    "[Atom One Dark]": {//雅蓝  
        "sideBar.background": "#282c34" //项目管理器背景色设为与代码区背景色相同  
    }  
}

以下是几种常用配置的预览图:

  • 左右统一的黑色主题

  • 左侧颜色更深的绿柔

  • 老HBuilder默认的深黄色主题

以下为分类的颜色明细说明:

颜色配置命名规则与vscode相同,方便从vscode提取外观主题。

文本编辑区

名称 描述
editor.foreground 编辑区前景颜色
editor.background 编辑区背景颜色

注意:本主题自定义是外观方面,不同代码的着色自定义未开放。

mac标题栏(只在mac os上生效)

名称 描述
titleBar.activeBackground 背景颜色
titleBar.activeForeground 前景颜色

工具栏

名称 描述
toolBar.background 工具栏背景颜色
toolBar.border 工具栏边框颜色
toolBar.hoverBackground 工具栏上图标被选中时的背景颜色

文件资源管理器

名称 描述
sideBar.background 背景颜色
sideBar.border 边框颜色

标签卡

名称 描述
editorGroupHeader.tabsBackground tabs背景颜色
tab.activeBackground 选中时的背景颜色
tab.border 边框颜色
tab.inactiveBackground 未选中时的背景颜色
tab.hoverBackground 鼠标滑过时的背景颜色
tab.inactiveForeground 未选中时的前景颜色
tab.activeBorder 选中时的边框颜色
tab.activeForeground 选中时的前景颜色
tab.unfocusedInactiveForeground 未选中分栏里未选中标签的前景颜色
tab.unfocusedHoverBackground 未选中分栏里鼠标滑过未选中标签的背景颜色
tab.unfocusedActiveForeground 未选中分栏里选中标签的前景颜色

分栏

名称 描述
editorGroup.border 分割线颜色
tab.unfocusedActiveForeground 未激活分组里选中标签前景色

按钮

名称 描述
button.background 按钮背景颜色
button.foreground 按钮前景颜色
button.hoverBackground 鼠标滑过按钮背景颜色

代码助手

名称 描述
editorSuggestWidget.background 助手弹窗背景颜色
editorSuggestWidget.border 助手弹窗边框颜色
editorSuggestWidget.selectedBackground 助手弹窗选中条目时背景颜色
editorSuggestWidget.link 助手链接颜色

文本框

名称 描述
input.background 文本框背景颜色
input.foreground 文本框前景颜色
input.border 文本框边框颜色
focusBorder 文本框有焦点时边框颜色

列表控件/树控件区

名称 描述
list.foreground 前景颜色
list.activeSelectionBackground 选中条目背景颜色
list.activeSelectionForeground 选中条目前景颜色
list.hoverBackground 鼠标滑过背景颜色
list.highlightForeground 高亮时前景颜色
list.inactiveSelectionBackground 未选中条目背景颜色
list.inactiveSelectionForeground 未选中条目前景颜色
explorer.file.status.modified svn/git项目修改状态前景色
explorer.file.status.untracked svn/git项目未跟踪状态前景色
explorer.file.status.added svn/git项目添加状态前景色
explorer.file.status.conflicted svn/git项目冲突状态前景色
explorer.file.status.rename svn/git项目重命名状态前景色
explorer.file.status.marktext svn/git项目标记前景色

文档结构图

名称 描述
outlineBackground 文档结构背景颜色

滚动条

名称 描述
scrollbarSlider.background 滚动条背景颜色
scrollbarSlider.hoverBackground 鼠标滑过滚动条背景颜色

预览按钮

名称 描述
extensionButton.prominentBackground 背景颜色
extensionButton.prominentForeground 前景颜色
extensionButton.border 边框颜色
extensionButton.prominentHoverBackground 鼠标滑过时的背景颜色
extensionButton.checkColor 选中时的前景颜色

搜索下拉框

名称 描述
inputValidation.infoBackground 下拉框背景颜色
inputList.hoverBackground 鼠标滑过item背景颜色
inputList.border 下拉框边框颜色
inputList.titleColor 下拉框左边文字颜色
inputList.foreground 下拉框右边文字颜色

设置

名称 描述
editor.background 设置页面背景颜色
inputOption.activeBorder 文本框有焦点时边框颜色
settings.textInputBackground 文本框背景颜色
settings.textInputBorder 文本框边框颜色
settings.textInputDisableBackground 文本框不可用背景颜色
settings.dropdownForeground combobox下拉列表前景颜色
settings.dropdownBorder combobox下拉列表边框颜色
settings.dropdownBackground combobox下拉列表背景颜色
settings.dropdownListBorder combobox item边框颜色

图片预览

名称 描述
imageview.background 浅色方格颜色
imageview.foreground 深色方格颜色

弹窗提示框

名称 描述
notifications.border 弹窗边框颜色
notifications.background 弹窗背景颜色
notifications.foreground 弹窗前景颜色
notification.buttonBorder 弹窗按钮边框颜色
notification.buttonBackground 弹窗按钮背景颜色
notification.buttonForeground 弹窗按钮前景颜色
notification.buttonPressedForeground 弹窗按钮按下前景颜色
notification.buttonPressedBackground 弹窗按钮按下背景颜色
notificationLink.foreground 弹窗链接颜色

文件对比

名称 描述
filediff.line.add 添加行背景颜色
filediff.line.delete 删除行背景颜色
filediff.inline.base 行内比较和右边行有不同字符,左边字符显示颜色
filediff.inline.modify 行内比较和左边行有不同字符,右边字符显示颜色

控制台

名称 描述
terminal.background 终端背景颜色
console.background 控制台背景颜色
panelTitle.activeForeground tab选中时的前景颜色
debug.foreground 前景颜色

查看svn/git日志

在svn/git项目上点击右键。只在mac上生效

名称 描述
logviewButton.background 按钮背景颜色
logviewButton.border 按钮边框颜色
logviewButton.hover 鼠标滑过按钮背景颜色
logviewButton.disable 按钮不可用背景颜色
logviewButtonDisable.border 按钮不可用边框颜色
logview.file.action.modified 选中已修改文件前景色
logview.file.action.deleted 选中已删除文件前景色
logview.file.action.added 选中已增加文件前景色
logview.file.action.rename 选中已重命名文件前景色

状态栏

名称 描述
statusBar.background 状态栏背景颜色
statusBar.foreground 状态栏前景颜色

右侧迷你地图

名称 描述
minimap.handle.background 迷你地图滑块背景

开发者自定义的主题汇总

欢迎更多开发者提交自己配置的主题并共享出来。

主题插件

HBuilderX自定义主题辅助工具

收起阅读 »

基于 three.js 的 3D 粒子动效实现

作者:个推web前端开发工程师 梁神

一、背景

粒子特效是为模拟现实中的水、火、雾、气等效果由各种三维软件开发的制作模块,原理是将无数的单个粒子组合使其呈现出固定形态,借由控制器、脚本来控制其整体或单个的运动,模拟出现真实的效果。three.js是用JavaScript编写的WebGL的第三方库,three.js提供了丰富的API帮助我们去实现3D动效,本文主要介绍如何使用three.js实现粒子过渡效果,以及基本的鼠标交互操作。(注:本文使用的关于three.js的API都是基于版本r98的。)

二、实现步骤

1. 创建渲染场景scene

scene实际上相当于一个三维空间,用于承载和显示我们所定义的一切,包括相机、物体、灯光等。在实际开发时为了方便观察可添加一些辅助工具,比如网格、坐标轴等。

scene = new THREE.Scene();
scene.fog = new THREE.Fog(0x05050c, 10, 60);
scene.add( new THREE.GridHelper( 2000, 1 ) ); // 添加网格

2. 添加照相机camera

THREE里面实现了几种相机:PerspectiveCamera(透视相机)、 OrthographicCamera(正交投影相机)、CubeCamera(立方体相机或全景相机)和 StereoCamera(3D相机)。本文介绍我们主要用到的 PerspectiveCamera(透视相机):

视觉效果是近大远小。

配置参数 PerspectiveCamera(fov, aspect, near, far)。

fov:相机的可视角度。

aspect:相机可视范围的长宽比。

near:相对于深度剪切面的远的距离。

far:相对于深度剪切面的远的距离。

camera = new THREE.PerspectiveCamera(45, window.innerWidth /window.innerHeight, 5, 100);
camera.position.set(10, -10, -40);
scene.add(camera);

3. 添加场景渲染需要的灯光

three.js里面实现的光源:AmbientLight(环境光)、DirectionalLight(平行光)、HemisphereLight(半球光)、PointLight(点光源)、RectAreaLight(平面光源)、SpotLight(聚光灯)等。配置光源参数时需要注意颜色的叠加效果,如环境光的颜色会直接作用于物体的当前颜色。各种光源的配置参数有些区别,下面是本文案例中会用到的二种光源。

let ambientLight = new THREE.AmbientLight(0x000000, 0.4);
scene.add(ambientLight);
let pointLight = new THREE.PointLight(0xe42107);
pointLight.castShadow = true;
pointLight.position.set(-10, -5, -10);
pointLight.distance = 20;
scene.add(pointLight);

4. 创建、导出并加载模型文件loader

创建模型,可以使用three.js editor进行创建或者用three.js的基础模型生成类进行生成,相对复杂的或者比较特殊的模型需要使用建模工具进行创建(c4d、3dmax等)。

使用three.js editor进行创建,可添加基本几何体,调整几何体的各种参数(位置、颜色、材质等)。

使用模型类生成。

let geometryCube = new THREE.BoxBufferGeometry( 1, 1, 1 );
let materialCube = new THREE.MeshBasicMaterial( {color: 0x00ff00} );
let cubeMesh = new THREE.Mesh( geometryCube, materialCube );
scene.add( cubeMesh );

导出需要的模型文件(此处使用的是 obj格式的模型文件)。

加载并解析模型文件数据。

let onProgress = function (xhr) {
if (xhr.lengthComputable) {
// 可进行计算得知模型加载进度
}
};
let onError = function () {};
particleSystem = new THREE.Group();
var texture = new THREE.TextureLoader().load('./point.png');
new THREE.OBJLoader().load('./model.obj', function (object) {
// object 模型文件数据
}, onProgress, onError);

5. 将导入到模型文件转换成粒子系统Points

获取模型的坐标值。

拷贝粒子坐标值到新建属性position1上 ,这个作为粒子过渡效果的最终坐标位置。

给粒子系统添加随机三维坐标值position,目的是把每个粒子位置打乱,设定起始位置。

let color = new THREE.Color('#ffffff');
let material = new THREE.PointsMaterial({
size: 0.2,
map: texture,
depthTest: false,
transparent: true
});
particleSystem= new THREE.Group();
let allCount = 0
for (let i = 0; i < object.children.length; i++) {
let name = object.children[i].name
let _attributes = object.children[i].geometry.attributes
let count = _attributes.position.count
_attributes.positionEnd = _attributes.position.clone()
_attributes.position1 = _attributes.position.clone()
for (let i = 0; i < count 3; i++) {
_attributes.position1.array[i]= Math.random()
100 - 50
}
let particles = new THREE.Points(object.children[i].geometry, material)
particleSystem.add(particles)
allCount += count
}
particleSystem.applyMatrix(new THREE.Matrix4().makeTranslation(-5, -5,-10));

6. 通过tween动画库实现粒子坐标从position到position1点转换

利用 TWEEN 的缓动算法计算出各个粒子每一次变化的坐标位置,从初始位置到结束位置时间设置为2s(可自定义),每次执行计算之后都需要将attributes的position属性设置为true,用来提醒场景需要更新,在下次渲染时,render会使用最新计算的值进行渲染。

let pos = {
val: 1
};
tween = new TWEEN.Tween(pos).to({
val: 0
}, 2500).easing(TWEEN.Easing.Quadratic.InOut).onUpdate(callback);
tween.onComplete(function () {
console.log('过渡完成complete')
})
tween.start();
function callback() {
let val = this.val;
let particles = particleSystem.children;
for (let i = 0; i < particles.length; i++) {
let attributes = particles[i].geometry.attributes
let name = particles[i].name
if (name.indexOf('
') === -1) {
let positionEnd =_attributes.positionEnd.array
let position1 =_attributes.position1.array
let count =_attributes.position.count
for (let j = 0; j < count 3; j++) {
_attributes.position.array[j] = position1[j]
val + positionEnd[j] * (1 - val)
}
}
_attributes.position.needsUpdate = true // 设置更新
}
}

7. 添加渲染场景render

创建容器。

定义render渲染器,设置各个参数。

将渲染器添加到容器里。

自定义的渲染函数 render,在渲染函数里面我们利用 TWEEN.update 去更新模型的状态。

调用自定义的循环动画执行函数 animate,利用requestAnimationFrame方法进行逐帧渲染。

let container = document.createElement('div');
document.body.appendChild(container);
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setClearColor(scene.fog.color);
renderer.setClearAlpha(0.8);
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement); // 添加webgl渲染器

function render() {
particleSystem.rotation.y += 0.0001;
TWEEN.update();
particleSystem.rotation.y += (mouseX + camera.rotation.x) * .00001;
camera.lookAt(new THREE.Vector3(-10, -5, -10))
controls.update();
renderer.render(scene, camera);
}
function animate() { // 开始循环执行渲染动画
requestAnimationFrame(animate);
render();
}

8. 添加鼠标操作事件实现角度控制

我们还可以添加鼠标操作事件实现角度控制,其中winX、winY分别为window的宽高的一半,当然具体的坐标位置可以根据自己的需求进行计算,具体的效果如下图所示。

document.addEventListener('mousemove', onDocumentMouseMove, false);
function onDocumentMouseMove(event) {
mouseX = (event.clientX - winX) / 2;
mouseY = (event.clientY - winY) / 2;
}

三、优化方案

1. 减少粒子数量

随着粒子数量的增加,需要的计算每个粒子的位置和大小将会非常耗时,可能会造成动画卡顿或出现页面假死的情况,所以我们在建立模型时可尽量减少粒子的数量,能够有效提升性能。

在以上示例中,我们改变导出模型的精细程度,可以得到不同数量的粒子系统,当粒子数量达到几十万甚至几百万的时候,在动画加载时可以感受到明显的卡顿现象,这主要是由于fps比较低,具体的对比效果如下图所示,左边粒子数量为30万,右边粒子数量为6万,可以明显看出左边跳帧明显,右边基本保持比较流畅的状态。

2. 采用GPU渲染方式

编写片元着色器代码,利用webgl可以为canvas提供硬件3D加速,浏览器可以更流畅地渲染页面。目前大多数设备都已经支持该方式,需要注意的是在低端的设备上由于硬件设备原因,渲染的速度可能不及基于cpu计算的方式渲染。

四、总结

综上所述,实现粒子动效的关键在于计算、维护每个粒子的位置状态,而three.js提供了较为便利的方法,可以用于渲染整个粒子场景。当粒子数量极为庞大时,想要实现较为流畅的动画效果需要注意优化代码、减少计算等,也可以通过提升硬件配置来达到效果。本文中的案例为大家展示了3D粒子动效如何实现,大家可以根据自己的实际需求去制作更炫酷的动态效果。

继续阅读 »

作者:个推web前端开发工程师 梁神

一、背景

粒子特效是为模拟现实中的水、火、雾、气等效果由各种三维软件开发的制作模块,原理是将无数的单个粒子组合使其呈现出固定形态,借由控制器、脚本来控制其整体或单个的运动,模拟出现真实的效果。three.js是用JavaScript编写的WebGL的第三方库,three.js提供了丰富的API帮助我们去实现3D动效,本文主要介绍如何使用three.js实现粒子过渡效果,以及基本的鼠标交互操作。(注:本文使用的关于three.js的API都是基于版本r98的。)

二、实现步骤

1. 创建渲染场景scene

scene实际上相当于一个三维空间,用于承载和显示我们所定义的一切,包括相机、物体、灯光等。在实际开发时为了方便观察可添加一些辅助工具,比如网格、坐标轴等。

scene = new THREE.Scene();
scene.fog = new THREE.Fog(0x05050c, 10, 60);
scene.add( new THREE.GridHelper( 2000, 1 ) ); // 添加网格

2. 添加照相机camera

THREE里面实现了几种相机:PerspectiveCamera(透视相机)、 OrthographicCamera(正交投影相机)、CubeCamera(立方体相机或全景相机)和 StereoCamera(3D相机)。本文介绍我们主要用到的 PerspectiveCamera(透视相机):

视觉效果是近大远小。

配置参数 PerspectiveCamera(fov, aspect, near, far)。

fov:相机的可视角度。

aspect:相机可视范围的长宽比。

near:相对于深度剪切面的远的距离。

far:相对于深度剪切面的远的距离。

camera = new THREE.PerspectiveCamera(45, window.innerWidth /window.innerHeight, 5, 100);
camera.position.set(10, -10, -40);
scene.add(camera);

3. 添加场景渲染需要的灯光

three.js里面实现的光源:AmbientLight(环境光)、DirectionalLight(平行光)、HemisphereLight(半球光)、PointLight(点光源)、RectAreaLight(平面光源)、SpotLight(聚光灯)等。配置光源参数时需要注意颜色的叠加效果,如环境光的颜色会直接作用于物体的当前颜色。各种光源的配置参数有些区别,下面是本文案例中会用到的二种光源。

let ambientLight = new THREE.AmbientLight(0x000000, 0.4);
scene.add(ambientLight);
let pointLight = new THREE.PointLight(0xe42107);
pointLight.castShadow = true;
pointLight.position.set(-10, -5, -10);
pointLight.distance = 20;
scene.add(pointLight);

4. 创建、导出并加载模型文件loader

创建模型,可以使用three.js editor进行创建或者用three.js的基础模型生成类进行生成,相对复杂的或者比较特殊的模型需要使用建模工具进行创建(c4d、3dmax等)。

使用three.js editor进行创建,可添加基本几何体,调整几何体的各种参数(位置、颜色、材质等)。

使用模型类生成。

let geometryCube = new THREE.BoxBufferGeometry( 1, 1, 1 );
let materialCube = new THREE.MeshBasicMaterial( {color: 0x00ff00} );
let cubeMesh = new THREE.Mesh( geometryCube, materialCube );
scene.add( cubeMesh );

导出需要的模型文件(此处使用的是 obj格式的模型文件)。

加载并解析模型文件数据。

let onProgress = function (xhr) {
if (xhr.lengthComputable) {
// 可进行计算得知模型加载进度
}
};
let onError = function () {};
particleSystem = new THREE.Group();
var texture = new THREE.TextureLoader().load('./point.png');
new THREE.OBJLoader().load('./model.obj', function (object) {
// object 模型文件数据
}, onProgress, onError);

5. 将导入到模型文件转换成粒子系统Points

获取模型的坐标值。

拷贝粒子坐标值到新建属性position1上 ,这个作为粒子过渡效果的最终坐标位置。

给粒子系统添加随机三维坐标值position,目的是把每个粒子位置打乱,设定起始位置。

let color = new THREE.Color('#ffffff');
let material = new THREE.PointsMaterial({
size: 0.2,
map: texture,
depthTest: false,
transparent: true
});
particleSystem= new THREE.Group();
let allCount = 0
for (let i = 0; i < object.children.length; i++) {
let name = object.children[i].name
let _attributes = object.children[i].geometry.attributes
let count = _attributes.position.count
_attributes.positionEnd = _attributes.position.clone()
_attributes.position1 = _attributes.position.clone()
for (let i = 0; i < count 3; i++) {
_attributes.position1.array[i]= Math.random()
100 - 50
}
let particles = new THREE.Points(object.children[i].geometry, material)
particleSystem.add(particles)
allCount += count
}
particleSystem.applyMatrix(new THREE.Matrix4().makeTranslation(-5, -5,-10));

6. 通过tween动画库实现粒子坐标从position到position1点转换

利用 TWEEN 的缓动算法计算出各个粒子每一次变化的坐标位置,从初始位置到结束位置时间设置为2s(可自定义),每次执行计算之后都需要将attributes的position属性设置为true,用来提醒场景需要更新,在下次渲染时,render会使用最新计算的值进行渲染。

let pos = {
val: 1
};
tween = new TWEEN.Tween(pos).to({
val: 0
}, 2500).easing(TWEEN.Easing.Quadratic.InOut).onUpdate(callback);
tween.onComplete(function () {
console.log('过渡完成complete')
})
tween.start();
function callback() {
let val = this.val;
let particles = particleSystem.children;
for (let i = 0; i < particles.length; i++) {
let attributes = particles[i].geometry.attributes
let name = particles[i].name
if (name.indexOf('
') === -1) {
let positionEnd =_attributes.positionEnd.array
let position1 =_attributes.position1.array
let count =_attributes.position.count
for (let j = 0; j < count 3; j++) {
_attributes.position.array[j] = position1[j]
val + positionEnd[j] * (1 - val)
}
}
_attributes.position.needsUpdate = true // 设置更新
}
}

7. 添加渲染场景render

创建容器。

定义render渲染器,设置各个参数。

将渲染器添加到容器里。

自定义的渲染函数 render,在渲染函数里面我们利用 TWEEN.update 去更新模型的状态。

调用自定义的循环动画执行函数 animate,利用requestAnimationFrame方法进行逐帧渲染。

let container = document.createElement('div');
document.body.appendChild(container);
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setClearColor(scene.fog.color);
renderer.setClearAlpha(0.8);
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement); // 添加webgl渲染器

function render() {
particleSystem.rotation.y += 0.0001;
TWEEN.update();
particleSystem.rotation.y += (mouseX + camera.rotation.x) * .00001;
camera.lookAt(new THREE.Vector3(-10, -5, -10))
controls.update();
renderer.render(scene, camera);
}
function animate() { // 开始循环执行渲染动画
requestAnimationFrame(animate);
render();
}

8. 添加鼠标操作事件实现角度控制

我们还可以添加鼠标操作事件实现角度控制,其中winX、winY分别为window的宽高的一半,当然具体的坐标位置可以根据自己的需求进行计算,具体的效果如下图所示。

document.addEventListener('mousemove', onDocumentMouseMove, false);
function onDocumentMouseMove(event) {
mouseX = (event.clientX - winX) / 2;
mouseY = (event.clientY - winY) / 2;
}

三、优化方案

1. 减少粒子数量

随着粒子数量的增加,需要的计算每个粒子的位置和大小将会非常耗时,可能会造成动画卡顿或出现页面假死的情况,所以我们在建立模型时可尽量减少粒子的数量,能够有效提升性能。

在以上示例中,我们改变导出模型的精细程度,可以得到不同数量的粒子系统,当粒子数量达到几十万甚至几百万的时候,在动画加载时可以感受到明显的卡顿现象,这主要是由于fps比较低,具体的对比效果如下图所示,左边粒子数量为30万,右边粒子数量为6万,可以明显看出左边跳帧明显,右边基本保持比较流畅的状态。

2. 采用GPU渲染方式

编写片元着色器代码,利用webgl可以为canvas提供硬件3D加速,浏览器可以更流畅地渲染页面。目前大多数设备都已经支持该方式,需要注意的是在低端的设备上由于硬件设备原因,渲染的速度可能不及基于cpu计算的方式渲染。

四、总结

综上所述,实现粒子动效的关键在于计算、维护每个粒子的位置状态,而three.js提供了较为便利的方法,可以用于渲染整个粒子场景。当粒子数量极为庞大时,想要实现较为流畅的动画效果需要注意优化代码、减少计算等,也可以通过提升硬件配置来达到效果。本文中的案例为大家展示了3D粒子动效如何实现,大家可以根据自己的实际需求去制作更炫酷的动态效果。

收起阅读 »

大数据时代,如何构建精准用户画像,直击精细化运营

移动互联网时代,精细化运营逐渐成为企业发展的重要竞争力,“用户画像”的概念也应运而生。用户画像是指,在大数据时代,企业通过对海量数据信息进行清洗、聚类、分析,将数据抽象成标签,再利用这些标签将用户形象具体化的过程。用户画像的建立能够帮助企业更好地为用户提供针对性的服务。
与之相应,越来越多的第三方大数据公司,也开始依托自身的数据积累,为客户提供用户画像的服务。比如个推旗下的用户画像产品,能够对用户线上和线下行为进行大数据分析,帮助APP开发者和运营者构建全面、精准、多维的用户画像体系。下文将以个推用户画像产品为例,详解“用户画像”的技术特点和使用价值。
用户画像的形成需要经历四个过程,数据积累、数据清洗、数据建模分析、数据产出。其中,数据清洗和数据建模统称数据处理。在经过数据处理之后,个推产出独特的冷、热、温数据维度,并分析用户的线上兴趣偏好和线下行为场景,形成用户画像。

一、用户画像用了哪些技术?

在数据处理阶段,个推用户画像产品的大数据计算架构采用了Kafka分布式发布订阅消息系统,具有高吞吐量、高稳定性的特点。数据清洗可利用HADOOP、SPARK来实现设备唯一性识别、行为数据的清洗等,去除冗余数据。这一过程支持交互计算和多种复杂算法,并支持数据实时/离线计算。

在数据建模的过程中,个推用户画像产品主要利用了机器学习中的聚类(无监督学习)和深度学习技术,让模型对用户行为数据主动学习,进行行为判断,由此产出用户标签。

经过数据的清洗和建模,个推用户画像会形成冷数据画像、温数据回溯、热数据场景和定制化标签四种画像。
冷数据画像,是指基于大数据的分析得出的用户属性,相对比较稳定,如用户的年龄层次、性别、常驻地等。“温数据”则可以回溯用户近期活跃的线上和线下场景,具有一定的时效性。“热数据”是指用户当下的场景及实时的用户特征,帮助APP运营者抓住稍纵即逝的营销机会。定制化标签是将个推数据与第三方数据结合起来,共同建模得出具有价值的特征标签。

二、如何构建用户画像?

“用户画像”的构建需要技术和业务人员的共同参与,以避免形式化的用户画像。个推构建用户画像时流程如下:
(1)标签体系设计。开发者需要先了解自身的数据,确定需要设计的标签形式。
(2)多数据源数据融合。在建设用户画像时,个推会整合自有海量数据以及该APP自身的数据。
(3)实现用户统一标识。多数情况下,APP的众多用户分布于不同的账号体系中,个推会将其统一标识,帮助APP打通账号,实现信息快速共享。
(4)用户画像特征层构建。即将每一个数据进行特征化。
(5)画像标签规则+算法建模。两者缺一不可,在实际的应用中,算法难以解决的问题,利用简单的规则也可以达到很好的效果。
(6)利用算法对所有用户打标签。
(7)画像质量监控。在实际的应用中,用户画像会产生一定的波动,为了解决这个问题,个推建设了相应的监控系统,对画像的质量进行监控。
总之,个推用户画像构建的整体流程,可以概括为三个部分:
第一,基础数据处理。基础数据包括用户设备信息、用户的线上APP偏好以及线下场景数据等。
第二,画像中间数据处理。处理结果包括线上APP偏好特征和线下场景特征等。
第三,画像信息表。表中应有四种信息:设备基础属性;用户基础画像,包括用户的性别、年龄层次、相关消费水平等;用户兴趣画像,即用户更有兴趣的方向,比如用户更偏好拼团类APP还是海淘类APP;用户其它画像等。
在个推用户画像构建的过程中,机器学习占据了较为重要的位置。机器学习主要应用在海量设备数据整理、数据清洗、数据存储的过程中。

三、用户画像能做什么?

用户画像对电商类、新闻资讯类APP的作用不言而喻,可以帮助APP打造精准推荐系统,实现千人千面的运营。
基于用户特征的个性化推荐
APP的运营者可以通过个推用户画像提供的性别、年龄层次、兴趣爱好等标签,分别展示不同的内容给用户,以达到精准化运营的目的。

基于用户特征指导内容推荐
基于用户特征指导内容的推荐是指找到与目标群体相似的用户群,并利用该相似用户群的行为特征对目标用户进行内容推荐,具体过程如下图:

在实现这一内容推荐的过程中,相似性建模技术起着不可忽视的作用。相似性建模可类比于聚类建模,是无监督学习中的一种。它可以寻找数据中的特征,把具有相同特征的数据聚集在一组,并赋予这些聚集在一起的数据相同的特征标签。根据这些特征标签,寻找具有这些特性的用户,给他们推送相同的内容。

这种推荐方式的优点是,它的自有特征是经过APP长期积淀而来,颗粒度更细,适用性更强,对用户的认识更全面,效果能持续提升。而且它还能针对APP所处行业与自身需求,量身定制匹配算法,让推荐更精准。
此外,个推用户画像能够结合第三方数据做定制化建模,通过双方共同建模得出显著价值和特征标签。这种标签增补的方式不仅能保证推送的内容更精准,同时也可以很大程度地提升流量价值。

四、开发者如何接入?

个推用户画像SDK的接入主要有两个方式:
• SDK集成:客户端集成个推用户画像SDK,进行初始化SDK后,返回给客户一个ID即GIUID(唯一身份标识), 此ID需要由客户端提交到客户服务器,然后服务器通过API接口传入GIUID进行查询用户画像标签数据。

• API接口调用:客户将应用名称、包名及服务端出口IP提供后,返回APP ID等相关信息。客户根据《个推用户画像数据服务接口文档》及《用户画像编码表》集成测试后方可调用API接口查询画像信息。

具体的集成文档参见以下链接:
Android:http://docs.getui.com/gexiang/start/android/
iOS:http://docs.getui.com/gexiang/start/ios/
服务端:http://docs.getui.com/gexiang/start/server/

了解用户,是为了更好地服务用户。正是APP开发者和运营者对用户认知的渴求促生了用户画像。而APP开发者只有把用户的需求放在最重要的位置,才能更好地优化用户的体验,留住用户。接入个推画像SDK,不仅可以帮助开发人员提高开发决策的效率,也可以帮助APP运营人员开展精细化运营,从而提升企业的营销效率和市场竞争力。

继续阅读 »

移动互联网时代,精细化运营逐渐成为企业发展的重要竞争力,“用户画像”的概念也应运而生。用户画像是指,在大数据时代,企业通过对海量数据信息进行清洗、聚类、分析,将数据抽象成标签,再利用这些标签将用户形象具体化的过程。用户画像的建立能够帮助企业更好地为用户提供针对性的服务。
与之相应,越来越多的第三方大数据公司,也开始依托自身的数据积累,为客户提供用户画像的服务。比如个推旗下的用户画像产品,能够对用户线上和线下行为进行大数据分析,帮助APP开发者和运营者构建全面、精准、多维的用户画像体系。下文将以个推用户画像产品为例,详解“用户画像”的技术特点和使用价值。
用户画像的形成需要经历四个过程,数据积累、数据清洗、数据建模分析、数据产出。其中,数据清洗和数据建模统称数据处理。在经过数据处理之后,个推产出独特的冷、热、温数据维度,并分析用户的线上兴趣偏好和线下行为场景,形成用户画像。

一、用户画像用了哪些技术?

在数据处理阶段,个推用户画像产品的大数据计算架构采用了Kafka分布式发布订阅消息系统,具有高吞吐量、高稳定性的特点。数据清洗可利用HADOOP、SPARK来实现设备唯一性识别、行为数据的清洗等,去除冗余数据。这一过程支持交互计算和多种复杂算法,并支持数据实时/离线计算。

在数据建模的过程中,个推用户画像产品主要利用了机器学习中的聚类(无监督学习)和深度学习技术,让模型对用户行为数据主动学习,进行行为判断,由此产出用户标签。

经过数据的清洗和建模,个推用户画像会形成冷数据画像、温数据回溯、热数据场景和定制化标签四种画像。
冷数据画像,是指基于大数据的分析得出的用户属性,相对比较稳定,如用户的年龄层次、性别、常驻地等。“温数据”则可以回溯用户近期活跃的线上和线下场景,具有一定的时效性。“热数据”是指用户当下的场景及实时的用户特征,帮助APP运营者抓住稍纵即逝的营销机会。定制化标签是将个推数据与第三方数据结合起来,共同建模得出具有价值的特征标签。

二、如何构建用户画像?

“用户画像”的构建需要技术和业务人员的共同参与,以避免形式化的用户画像。个推构建用户画像时流程如下:
(1)标签体系设计。开发者需要先了解自身的数据,确定需要设计的标签形式。
(2)多数据源数据融合。在建设用户画像时,个推会整合自有海量数据以及该APP自身的数据。
(3)实现用户统一标识。多数情况下,APP的众多用户分布于不同的账号体系中,个推会将其统一标识,帮助APP打通账号,实现信息快速共享。
(4)用户画像特征层构建。即将每一个数据进行特征化。
(5)画像标签规则+算法建模。两者缺一不可,在实际的应用中,算法难以解决的问题,利用简单的规则也可以达到很好的效果。
(6)利用算法对所有用户打标签。
(7)画像质量监控。在实际的应用中,用户画像会产生一定的波动,为了解决这个问题,个推建设了相应的监控系统,对画像的质量进行监控。
总之,个推用户画像构建的整体流程,可以概括为三个部分:
第一,基础数据处理。基础数据包括用户设备信息、用户的线上APP偏好以及线下场景数据等。
第二,画像中间数据处理。处理结果包括线上APP偏好特征和线下场景特征等。
第三,画像信息表。表中应有四种信息:设备基础属性;用户基础画像,包括用户的性别、年龄层次、相关消费水平等;用户兴趣画像,即用户更有兴趣的方向,比如用户更偏好拼团类APP还是海淘类APP;用户其它画像等。
在个推用户画像构建的过程中,机器学习占据了较为重要的位置。机器学习主要应用在海量设备数据整理、数据清洗、数据存储的过程中。

三、用户画像能做什么?

用户画像对电商类、新闻资讯类APP的作用不言而喻,可以帮助APP打造精准推荐系统,实现千人千面的运营。
基于用户特征的个性化推荐
APP的运营者可以通过个推用户画像提供的性别、年龄层次、兴趣爱好等标签,分别展示不同的内容给用户,以达到精准化运营的目的。

基于用户特征指导内容推荐
基于用户特征指导内容的推荐是指找到与目标群体相似的用户群,并利用该相似用户群的行为特征对目标用户进行内容推荐,具体过程如下图:

在实现这一内容推荐的过程中,相似性建模技术起着不可忽视的作用。相似性建模可类比于聚类建模,是无监督学习中的一种。它可以寻找数据中的特征,把具有相同特征的数据聚集在一组,并赋予这些聚集在一起的数据相同的特征标签。根据这些特征标签,寻找具有这些特性的用户,给他们推送相同的内容。

这种推荐方式的优点是,它的自有特征是经过APP长期积淀而来,颗粒度更细,适用性更强,对用户的认识更全面,效果能持续提升。而且它还能针对APP所处行业与自身需求,量身定制匹配算法,让推荐更精准。
此外,个推用户画像能够结合第三方数据做定制化建模,通过双方共同建模得出显著价值和特征标签。这种标签增补的方式不仅能保证推送的内容更精准,同时也可以很大程度地提升流量价值。

四、开发者如何接入?

个推用户画像SDK的接入主要有两个方式:
• SDK集成:客户端集成个推用户画像SDK,进行初始化SDK后,返回给客户一个ID即GIUID(唯一身份标识), 此ID需要由客户端提交到客户服务器,然后服务器通过API接口传入GIUID进行查询用户画像标签数据。

• API接口调用:客户将应用名称、包名及服务端出口IP提供后,返回APP ID等相关信息。客户根据《个推用户画像数据服务接口文档》及《用户画像编码表》集成测试后方可调用API接口查询画像信息。

具体的集成文档参见以下链接:
Android:http://docs.getui.com/gexiang/start/android/
iOS:http://docs.getui.com/gexiang/start/ios/
服务端:http://docs.getui.com/gexiang/start/server/

了解用户,是为了更好地服务用户。正是APP开发者和运营者对用户认知的渴求促生了用户画像。而APP开发者只有把用户的需求放在最重要的位置,才能更好地优化用户的体验,留住用户。接入个推画像SDK,不仅可以帮助开发人员提高开发决策的效率,也可以帮助APP运营人员开展精细化运营,从而提升企业的营销效率和市场竞争力。

收起阅读 »