HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

关于小米手机中 plus.ui.Toast 存在被自动添加 HBuilder 字样的解决方法

toast

前几天就遇到了这个问题,没有时间去处理这个小问题,所以就放过一边等待最后去解决,今天有闲下来了就一并解决这些小问题

遇到的小问题:
遇到的小问题

解决以后:
解决以后

解决方法:

var Toast = plus.android.importClass("android.widget.Toast");  
var mToast = Toast.makeText(plus.android.runtimeMainActivity(), null, Toast.LENGTH_SHORT);  
mToast.setText('Hello World!');  
mToast.show();
继续阅读 »

前几天就遇到了这个问题,没有时间去处理这个小问题,所以就放过一边等待最后去解决,今天有闲下来了就一并解决这些小问题

遇到的小问题:
遇到的小问题

解决以后:
解决以后

解决方法:

var Toast = plus.android.importClass("android.widget.Toast");  
var mToast = Toast.makeText(plus.android.runtimeMainActivity(), null, Toast.LENGTH_SHORT);  
mToast.setText('Hello World!');  
mToast.show();
收起阅读 »

解决上拉加载

当总页数小于等于pageSize时,首次加载需要禁用上拉加载,一般使用mui('#pullrefreshContainer').pullRefresh().disablePullupToRefresh();但是该方法时好时坏,经常有解决办法是使用定时器延时执行该方法,实际上仍不能解决mui存在的bug;以下是我的解决办法,供大家参考:
1、定义全局变量pullupToRefresh = true;//能否上拉加载
2、在ajax的回调函数里判断,如果总页数是否小于等于pageSize,则pullupToRefresh = false,否则pullupToRefresh = true;
3、在上拉加载方法里首先判断pullupToRefresh的值,当pullupToRefresh = true时才执行下拉刷新

继续阅读 »

当总页数小于等于pageSize时,首次加载需要禁用上拉加载,一般使用mui('#pullrefreshContainer').pullRefresh().disablePullupToRefresh();但是该方法时好时坏,经常有解决办法是使用定时器延时执行该方法,实际上仍不能解决mui存在的bug;以下是我的解决办法,供大家参考:
1、定义全局变量pullupToRefresh = true;//能否上拉加载
2、在ajax的回调函数里判断,如果总页数是否小于等于pageSize,则pullupToRefresh = false,否则pullupToRefresh = true;
3、在上拉加载方法里首先判断pullupToRefresh的值,当pullupToRefresh = true时才执行下拉刷新

收起阅读 »

接兼职\外包\咨询类

个人插件页LinkedIn个人网站

uni框架1年经验(熟悉vue生态,熟读源码),nodejs经验2年(express+mongodb+redis)
熟悉js原生开发与安卓原生插件开发

可接外包:

  • 网站项目(静态/动态)
  • uniapp项目
  • 服务器应用(数据库的Restful api设计或websocket服务器设计)
  • 普通组件
  • 第三方原生lib转化为uniapp的原生插件
  • 应用中接入ai功能
  • BUG消除

可全部typescript开发,可以个人或公司名义合作,可签合同

如有需求请联系报价
联系方式:qq527501080 email :snowolfjay@qq.com

继续阅读 »

个人插件页LinkedIn个人网站

uni框架1年经验(熟悉vue生态,熟读源码),nodejs经验2年(express+mongodb+redis)
熟悉js原生开发与安卓原生插件开发

可接外包:

  • 网站项目(静态/动态)
  • uniapp项目
  • 服务器应用(数据库的Restful api设计或websocket服务器设计)
  • 普通组件
  • 第三方原生lib转化为uniapp的原生插件
  • 应用中接入ai功能
  • BUG消除

可全部typescript开发,可以个人或公司名义合作,可签合同

如有需求请联系报价
联系方式:qq527501080 email :snowolfjay@qq.com

收起阅读 »

uni-app调用Android文件系统

最近用 uni-app做多媒体上传功能, 文件选择,视频,图片官方分别有uni.chooseVideo和uni.chooseImage接口,很方便,但是涉及到其他文件类型选择比如音频,就没有现成的接口。 在网上搜索了一圈,终于找到了通过Native.js调用原生文件系统的方法 。 原博客地址:https://www.cnblogs.com/lizhao123/p/9951581.html
我结合了作者的方法,简单的封装为pickFile.js,调用起来很方便。用法很简单,把该文件放到你的项目JS目录下,通过 import {pickFile} from '@/js/common/pickFile.js' 方式引入,然后

pickFile.PickFile(function(audioSrc){  
     console.log(audioSrc)  
    _this.audioSrc = 'file://' + audioSrc;  
    // _this.audioSrc = 'https://img-cdn-qiniu.dcloud.net.cn/uniapp/audio/music.mp3'  
    // 这里就会得到 你选择的文件路径。拿到路径后,你可以用uni.uploadFile  进行上传。  
}, 'audio/*');  
//这里第一个参数是 回调函数,第二个是你要选择的文件类 "image/*","audio/*","video/*;image/*"

以下是我修改后的pickFile.js 源码。

var pickFile = {  
    //调用原生文件系统管理器并选取文件获取文件地址  
    PickFile:function(callback, acceptType) { //acceptType为你要查的文件类型"image/*","audio/*","video/*;image/*"  // intent.setType("image/*");//intent.setType("audio/*"); //选择音频//intent.setType("video/*;image/*"); //选择视频 (mp4 3gp 是android支持的视频格式)  
        var CODE_REQUEST = 1000;  
        var main = plus.android.runtimeMainActivity();  
        if (plus.os.name == 'Android') {  
            var Intent = plus.android.importClass('android.content.Intent');  
            var intent = new Intent(Intent.ACTION_GET_CONTENT);  
            intent.addCategory(Intent.CATEGORY_OPENABLE);  
            if (acceptType) {  
                intent.setType(acceptType);  
            } else {  
                intent.setType("*/*");  
            }  
            let _this = pickFile;  
            main.onActivityResult = function(requestCode, resultCode, data) {  
                if (requestCode == CODE_REQUEST) {  
                    var uri = data.getData();  
                    plus.android.importClass(uri);  
                    var Build = plus.android.importClass('android.os.Build');  
                    var isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;  

                    var DocumentsContract = plus.android.importClass('android.provider.DocumentsContract');  
                    // DocumentProvider  
                    if (isKitKat && DocumentsContract.isDocumentUri(main, uri)) {  
                        console.log("版本大于 4.4 ");  
                        // ExternalStorageProvider  
                        if ("com.android.externalstorage.documents" == uri.getAuthority()) {  
                            var docId = DocumentsContract.getDocumentId(uri);  
                            var split = docId.split(":");  
                            var type = split[0];  

                            if ("primary" == type) {  
                                var Environment = plus.android.importClass('android.os.Environment');  
                                callback(Environment.getExternalStorageDirectory() + "/" + split[1]);  
                            } else {  
                                var System = plus.android.importClass('java.lang.System');  
                                var sdPath = System.getenv("SECONDARY_STORAGE");  
                                if (sdPath) {  
                                    callback(sdPath + "/" + split[1]);  
                                }  
                            }  
                        }  
                        // DownloadsProvider  
                        else if ("com.android.providers.downloads.documents" == uri.getAuthority()) {  
                            var id = DocumentsContract.getDocumentId(uri);  
                            var ContentUris = plus.android.importClass('android.content.ContentUris');  
                            var contentUri = ContentUris.withAppendedId(  
                                //    Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));  
                                Uri.parse("content://downloads/public_downloads"), id);  
                            callback(_this.getDataColumn(main, contentUri, null, null));  
                        }  
                        // MediaProvider  
                        else if ("com.android.providers.media.documents" == uri.getAuthority()) {  
                            var docId = DocumentsContract.getDocumentId(uri);  
                            var split = docId.split(":");  
                            var type = split[0];  

                            var MediaStore = plus.android.importClass('android.provider.MediaStore');  
                            if ("image" == type) {  
                                contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;  
                            } else if ("video" == type) {  
                                contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;  
                            } else if ("audio" == type) {  
                                contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;  
                            }  

                            var selection = "_id=?";  
                            var selectionArgs = new Array();  
                            selectionArgs[0] = split[1];  

                            callback(_this.getDataColumn(main, contentUri, selection, selectionArgs));  
                        }  
                    }  
                    // MediaStore (and general)  
                    else if ("content" == uri.getScheme()) {  
                        callback(_this.getDataColumn(main, uri, null, null));  
                    }  
                    // File  
                    else if ("file" == uri.getScheme()) {  
                        callback(uri.getPath());  
                    }  
                }  
            }  
            main.startActivityForResult(intent, CODE_REQUEST);  
        }  
    },  

    getDataColumn:function(main, uri, selection, selectionArgs) {  
        plus.android.importClass(main.getContentResolver());  
        let cursor = main.getContentResolver().query(uri, ['_data'], selection, selectionArgs,  
            null);  
        plus.android.importClass(cursor);  
        if (cursor != null && cursor.moveToFirst()) {  
            var column_index = cursor.getColumnIndexOrThrow('_data');  
            var result = cursor.getString(column_index)  
            cursor.close();  
            return result;  
        }  
        return null;  
    }  
}  

module.exports = {  
    pickFile: pickFile  
}  
继续阅读 »

最近用 uni-app做多媒体上传功能, 文件选择,视频,图片官方分别有uni.chooseVideo和uni.chooseImage接口,很方便,但是涉及到其他文件类型选择比如音频,就没有现成的接口。 在网上搜索了一圈,终于找到了通过Native.js调用原生文件系统的方法 。 原博客地址:https://www.cnblogs.com/lizhao123/p/9951581.html
我结合了作者的方法,简单的封装为pickFile.js,调用起来很方便。用法很简单,把该文件放到你的项目JS目录下,通过 import {pickFile} from '@/js/common/pickFile.js' 方式引入,然后

pickFile.PickFile(function(audioSrc){  
     console.log(audioSrc)  
    _this.audioSrc = 'file://' + audioSrc;  
    // _this.audioSrc = 'https://img-cdn-qiniu.dcloud.net.cn/uniapp/audio/music.mp3'  
    // 这里就会得到 你选择的文件路径。拿到路径后,你可以用uni.uploadFile  进行上传。  
}, 'audio/*');  
//这里第一个参数是 回调函数,第二个是你要选择的文件类 "image/*","audio/*","video/*;image/*"

以下是我修改后的pickFile.js 源码。

var pickFile = {  
    //调用原生文件系统管理器并选取文件获取文件地址  
    PickFile:function(callback, acceptType) { //acceptType为你要查的文件类型"image/*","audio/*","video/*;image/*"  // intent.setType("image/*");//intent.setType("audio/*"); //选择音频//intent.setType("video/*;image/*"); //选择视频 (mp4 3gp 是android支持的视频格式)  
        var CODE_REQUEST = 1000;  
        var main = plus.android.runtimeMainActivity();  
        if (plus.os.name == 'Android') {  
            var Intent = plus.android.importClass('android.content.Intent');  
            var intent = new Intent(Intent.ACTION_GET_CONTENT);  
            intent.addCategory(Intent.CATEGORY_OPENABLE);  
            if (acceptType) {  
                intent.setType(acceptType);  
            } else {  
                intent.setType("*/*");  
            }  
            let _this = pickFile;  
            main.onActivityResult = function(requestCode, resultCode, data) {  
                if (requestCode == CODE_REQUEST) {  
                    var uri = data.getData();  
                    plus.android.importClass(uri);  
                    var Build = plus.android.importClass('android.os.Build');  
                    var isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;  

                    var DocumentsContract = plus.android.importClass('android.provider.DocumentsContract');  
                    // DocumentProvider  
                    if (isKitKat && DocumentsContract.isDocumentUri(main, uri)) {  
                        console.log("版本大于 4.4 ");  
                        // ExternalStorageProvider  
                        if ("com.android.externalstorage.documents" == uri.getAuthority()) {  
                            var docId = DocumentsContract.getDocumentId(uri);  
                            var split = docId.split(":");  
                            var type = split[0];  

                            if ("primary" == type) {  
                                var Environment = plus.android.importClass('android.os.Environment');  
                                callback(Environment.getExternalStorageDirectory() + "/" + split[1]);  
                            } else {  
                                var System = plus.android.importClass('java.lang.System');  
                                var sdPath = System.getenv("SECONDARY_STORAGE");  
                                if (sdPath) {  
                                    callback(sdPath + "/" + split[1]);  
                                }  
                            }  
                        }  
                        // DownloadsProvider  
                        else if ("com.android.providers.downloads.documents" == uri.getAuthority()) {  
                            var id = DocumentsContract.getDocumentId(uri);  
                            var ContentUris = plus.android.importClass('android.content.ContentUris');  
                            var contentUri = ContentUris.withAppendedId(  
                                //    Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));  
                                Uri.parse("content://downloads/public_downloads"), id);  
                            callback(_this.getDataColumn(main, contentUri, null, null));  
                        }  
                        // MediaProvider  
                        else if ("com.android.providers.media.documents" == uri.getAuthority()) {  
                            var docId = DocumentsContract.getDocumentId(uri);  
                            var split = docId.split(":");  
                            var type = split[0];  

                            var MediaStore = plus.android.importClass('android.provider.MediaStore');  
                            if ("image" == type) {  
                                contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;  
                            } else if ("video" == type) {  
                                contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;  
                            } else if ("audio" == type) {  
                                contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;  
                            }  

                            var selection = "_id=?";  
                            var selectionArgs = new Array();  
                            selectionArgs[0] = split[1];  

                            callback(_this.getDataColumn(main, contentUri, selection, selectionArgs));  
                        }  
                    }  
                    // MediaStore (and general)  
                    else if ("content" == uri.getScheme()) {  
                        callback(_this.getDataColumn(main, uri, null, null));  
                    }  
                    // File  
                    else if ("file" == uri.getScheme()) {  
                        callback(uri.getPath());  
                    }  
                }  
            }  
            main.startActivityForResult(intent, CODE_REQUEST);  
        }  
    },  

    getDataColumn:function(main, uri, selection, selectionArgs) {  
        plus.android.importClass(main.getContentResolver());  
        let cursor = main.getContentResolver().query(uri, ['_data'], selection, selectionArgs,  
            null);  
        plus.android.importClass(cursor);  
        if (cursor != null && cursor.moveToFirst()) {  
            var column_index = cursor.getColumnIndexOrThrow('_data');  
            var result = cursor.getString(column_index)  
            cursor.close();  
            return result;  
        }  
        return null;  
    }  
}  

module.exports = {  
    pickFile: pickFile  
}  
收起阅读 »

uniapp 在H5APP内长按图片保存到系统相册

原文链接:https://www.hebaocun.com/blog/97.html

var bitmap = new plus.nativeObj.Bitmap();
// 加载海报图片
bitmap.loadBase64Data(_this.poster, function() {
//保存到系统相册
bitmap.save("doc/poster"+_this.coupon.item_id+".jpg", {
overwrite: true, //是否覆盖已有图片, true 是
quality: 100 //图片质量,1-100 默认50, 100质量最高
}, function(e) {
//重点就是这里, 需要将bitmap保存的临时路径再使用saveImageToPhotosAlbum进行保存
uni.saveImageToPhotosAlbum({
filePath: e.target,
success: function () {
uni.showToast({
title: "图片保存成功",
icon: "none"
});
}
});
}, function(e) {
uni.showToast({
title: "图片保存失败",
icon: "none"
});
});
}, function(e) {
uni.showToast({
title: "图片保存失败",
icon: "none"
});
});

继续阅读 »

原文链接:https://www.hebaocun.com/blog/97.html

var bitmap = new plus.nativeObj.Bitmap();
// 加载海报图片
bitmap.loadBase64Data(_this.poster, function() {
//保存到系统相册
bitmap.save("doc/poster"+_this.coupon.item_id+".jpg", {
overwrite: true, //是否覆盖已有图片, true 是
quality: 100 //图片质量,1-100 默认50, 100质量最高
}, function(e) {
//重点就是这里, 需要将bitmap保存的临时路径再使用saveImageToPhotosAlbum进行保存
uni.saveImageToPhotosAlbum({
filePath: e.target,
success: function () {
uni.showToast({
title: "图片保存成功",
icon: "none"
});
}
});
}, function(e) {
uni.showToast({
title: "图片保存失败",
icon: "none"
});
});
}, function(e) {
uni.showToast({
title: "图片保存失败",
icon: "none"
});
});

收起阅读 »

对uniapp的吐槽

uniapp

举例:
一、原生样式的问题
就拿radio来说吧 内置的样式改不了 最后我用view模拟圆框 在里面套了个对勾来实现的

还有其他各种原生组件内置样式 有些能改 有些不能改 能改也麻烦 为什么不跟前端页面似的 让用户自己选择引不引用uniapp的样式?

二、原生DOM的问题
平时最烦的就是小程序 不允许用DOM有多不方便 知道吗(直接操作DOM的速度并不慢 说直接操作DOM慢的都是菜)
现在学小程序 不允许用DOM 还不如以前开发速度快
现在的时间都耽误在各种修改样式上

三、各种意外BUG
比如我封装了一个组件 给它一个方法当做props属性 然后在组件里调用这个方法 在H5端一切正常 在APP就不行 报错

吐完槽,说一下优点,就是文档比以前好了,这是唯一的优点。

希望贵司能相信用户,把控制权都放开,比如原生样式这个,能让用户自由选择用不用。
DOM就不指望了,唉 愁

继续阅读 »

举例:
一、原生样式的问题
就拿radio来说吧 内置的样式改不了 最后我用view模拟圆框 在里面套了个对勾来实现的

还有其他各种原生组件内置样式 有些能改 有些不能改 能改也麻烦 为什么不跟前端页面似的 让用户自己选择引不引用uniapp的样式?

二、原生DOM的问题
平时最烦的就是小程序 不允许用DOM有多不方便 知道吗(直接操作DOM的速度并不慢 说直接操作DOM慢的都是菜)
现在学小程序 不允许用DOM 还不如以前开发速度快
现在的时间都耽误在各种修改样式上

三、各种意外BUG
比如我封装了一个组件 给它一个方法当做props属性 然后在组件里调用这个方法 在H5端一切正常 在APP就不行 报错

吐完槽,说一下优点,就是文档比以前好了,这是唯一的优点。

希望贵司能相信用户,把控制权都放开,比如原生样式这个,能让用户自由选择用不用。
DOM就不指望了,唉 愁

收起阅读 »

关于微信登陆获取code的方式。抄代码就可以了。

//全局申请一个:var auths=null;

// #ifdef APP-PLUS

plus.oauth.getServices(function(servies){  
    auths = servies;  
    var s = auths[0];  
    if( !s.authResult ){  

        s.authorize(function(e){  
            console.log('获取code!' + JSON.stringify(e));  
            uni.showModal({ title: '用户信息',content:  e.code,});    

        }, function(e){  
            console.log('登陆认证失败!');  
            uni.showModal({ title: '认证失败1',content: JSON.stringify(e),});     
        },  {scope:'snsapi_userinfo',state:'123'});  

    }else{  
        console.log('已经登陆认证');  

    }  

}, function(e){console.log("获取服务列表失败:"+JSON.stringify(e));})  

// #endif

如果会返回错误的话。建议以下:

第一,appid一定要对。是开放平台那里取,如果您自己是后台管理员就没有关系。如果您是从客户那里或是别的管理员,那就要非常注意了。我为了这个问题坑了几天。死活不成功,但是我自己亲自向微信公众平台申请了一个应用,一次成功。说明给的appid根本就是不正确的。

第二,签名一定要正确。应用在手机上安装后,用微信的工具去获取一般这步可能不会错。

第三,包名。一定不能出错。基本上这三步Ok。就能顺利进行授权。

继续阅读 »

//全局申请一个:var auths=null;

// #ifdef APP-PLUS

plus.oauth.getServices(function(servies){  
    auths = servies;  
    var s = auths[0];  
    if( !s.authResult ){  

        s.authorize(function(e){  
            console.log('获取code!' + JSON.stringify(e));  
            uni.showModal({ title: '用户信息',content:  e.code,});    

        }, function(e){  
            console.log('登陆认证失败!');  
            uni.showModal({ title: '认证失败1',content: JSON.stringify(e),});     
        },  {scope:'snsapi_userinfo',state:'123'});  

    }else{  
        console.log('已经登陆认证');  

    }  

}, function(e){console.log("获取服务列表失败:"+JSON.stringify(e));})  

// #endif

如果会返回错误的话。建议以下:

第一,appid一定要对。是开放平台那里取,如果您自己是后台管理员就没有关系。如果您是从客户那里或是别的管理员,那就要非常注意了。我为了这个问题坑了几天。死活不成功,但是我自己亲自向微信公众平台申请了一个应用,一次成功。说明给的appid根本就是不正确的。

第二,签名一定要正确。应用在手机上安装后,用微信的工具去获取一般这步可能不会错。

第三,包名。一定不能出错。基本上这三步Ok。就能顺利进行授权。

收起阅读 »

uni-app自定义组件模式开发注意事项

新框架 自定义组件模式

uni-app 自 1.8版本开始,新增支持自定义组件模式,该编译模式组件性能更高,支持更多的Vue语法。

请开发者尽快升级老版项目为自定义组件模式,老版的模板编译模式将不再维护。

Tips: uni-app 不同编译模式差异,参考:https://ask.dcloud.net.cn/article/35843

开发者启用自定义组件模式后,在进行自定义组件开发(页面开发不影响)时,需注意如下约束

  • id 为保留属性名,不能作为 props 传递,微信小程序自定义组件限制
  • props 中不能定义 data 为属性名,百度小程序限制
  • props 不支持 date 类型数据传递,微信小程序自定义组件限制
  • uni.createSelectorQuery()uni.createIntersectionObserver() 的调整
// 错误  
uni.createSelectorQuery();  
uni.createIntersectionObserver();  

// 正确  
uni.createSelectorQuery().in(this);                                                        
uni.createIntersectionObserver(this, options);
  • uni.createCanvasContext() 的调整
// 错误  
uni.createCanvasContext();  

// 正确  
uni.createCanvasContext('#canvas',this);
  • Array.length 改为计算属性
// 错误  
<view v-if="{Array.length >= 0}"></view>  

// 正确  
<view v-if="{count}" ></view>  
computed: {  
    count() {  
        return (Array.length >= 0)  
    }  
}
  • uParse 富文本解析组件的调整
    优化了 uParse 组件 的性能,如老项目有使用,请直接替换最新组件,使用方式不变:https://ext.dcloud.net.cn/plugin?id=183
    其实插件市场有更多三方增强的uparser组件可用,可以搜一下。

  • ECharts 图表组件的调整
    替换最新的 ucharts 组件, 源码地址:https://ext.dcloud.net.cn/plugin?id=271
    使用方式参考 :http://doc.ucharts.cn/1074673
    其实插件市场有更多优秀的三方增强的图表组件可用,可以搜一下。

  • css 调整
    在编译成微信小程序原生组件的时候,会在组件外增加一层父节点。有可能样式会受到影响,比较典型的就是 flex 布局,请在微信端完成一遍测试。
    在字节跳动小程序组件内引入字体会失效,解决方式:在 App.vue 内的 style 节点引入字体。

  • 组件内 css 选择器使用
    在微信小程序真机运行时,组件内如果使用了 tagName, ID, attribute 三种选择器时,会导致控制台出现告警信息。 所以我们在编写组件时,需要尽量避免使用上述三种选择器。

  • 组件生命周期
    编译成原生组件的时候,组件的生命周期请严格参考 组件生命周期 中的说明。
    需要注意的是,组件不支持 onLoad 等页面的生命周期。

  • 仅支持解构插槽 Prop(支持设置默认值,但不提供重命名)

<!—不支持—>  
<template v-slot:default="slotProps">  
    {{ slotProps.user.firstName }}  
</template>  
<!—支持—>  
<template v-slot:default="{user}">  
    {{ user.firstName }}  
</template>
  • 组件中引用图片等静态资源时,一定要使用绝对路径,即 /static/logo.png 这样。其实,无论是页面还是组件,引用静态资源时统一采用绝对路径的方式是最优方案。

App额外注意事项

  • App使用自定义组件模式,Android会增加6M左右的包体积。原因是App端增加了一个独立的v8以减少js的阻塞。iOS不变化,因为iOS的jscore是iOS自带的。
  • 离线打包的项目需要在原生工程里引入 :离线sdk包里的liblibWeex.a库 和 weex-main-jsfm.js 和 weex-polyfill.js文件。
  • 在自定义组件编译模式下,HBuilderX 2.4.0+ 支持 crypto.getRandomValues 方法,一些区块链应用的框架会用到此api
  • 在自定义组件编译模式下,uni.request 和 websocket 暂时不支持传输 ArrayBuffer 类型的数据,且 websocket 只能创建一个连接,有需求的用户可改用老的模板编译模式。
继续阅读 »

uni-app 自 1.8版本开始,新增支持自定义组件模式,该编译模式组件性能更高,支持更多的Vue语法。

请开发者尽快升级老版项目为自定义组件模式,老版的模板编译模式将不再维护。

Tips: uni-app 不同编译模式差异,参考:https://ask.dcloud.net.cn/article/35843

开发者启用自定义组件模式后,在进行自定义组件开发(页面开发不影响)时,需注意如下约束

  • id 为保留属性名,不能作为 props 传递,微信小程序自定义组件限制
  • props 中不能定义 data 为属性名,百度小程序限制
  • props 不支持 date 类型数据传递,微信小程序自定义组件限制
  • uni.createSelectorQuery()uni.createIntersectionObserver() 的调整
// 错误  
uni.createSelectorQuery();  
uni.createIntersectionObserver();  

// 正确  
uni.createSelectorQuery().in(this);                                                        
uni.createIntersectionObserver(this, options);
  • uni.createCanvasContext() 的调整
// 错误  
uni.createCanvasContext();  

// 正确  
uni.createCanvasContext('#canvas',this);
  • Array.length 改为计算属性
// 错误  
<view v-if="{Array.length >= 0}"></view>  

// 正确  
<view v-if="{count}" ></view>  
computed: {  
    count() {  
        return (Array.length >= 0)  
    }  
}
  • uParse 富文本解析组件的调整
    优化了 uParse 组件 的性能,如老项目有使用,请直接替换最新组件,使用方式不变:https://ext.dcloud.net.cn/plugin?id=183
    其实插件市场有更多三方增强的uparser组件可用,可以搜一下。

  • ECharts 图表组件的调整
    替换最新的 ucharts 组件, 源码地址:https://ext.dcloud.net.cn/plugin?id=271
    使用方式参考 :http://doc.ucharts.cn/1074673
    其实插件市场有更多优秀的三方增强的图表组件可用,可以搜一下。

  • css 调整
    在编译成微信小程序原生组件的时候,会在组件外增加一层父节点。有可能样式会受到影响,比较典型的就是 flex 布局,请在微信端完成一遍测试。
    在字节跳动小程序组件内引入字体会失效,解决方式:在 App.vue 内的 style 节点引入字体。

  • 组件内 css 选择器使用
    在微信小程序真机运行时,组件内如果使用了 tagName, ID, attribute 三种选择器时,会导致控制台出现告警信息。 所以我们在编写组件时,需要尽量避免使用上述三种选择器。

  • 组件生命周期
    编译成原生组件的时候,组件的生命周期请严格参考 组件生命周期 中的说明。
    需要注意的是,组件不支持 onLoad 等页面的生命周期。

  • 仅支持解构插槽 Prop(支持设置默认值,但不提供重命名)

<!—不支持—>  
<template v-slot:default="slotProps">  
    {{ slotProps.user.firstName }}  
</template>  
<!—支持—>  
<template v-slot:default="{user}">  
    {{ user.firstName }}  
</template>
  • 组件中引用图片等静态资源时,一定要使用绝对路径,即 /static/logo.png 这样。其实,无论是页面还是组件,引用静态资源时统一采用绝对路径的方式是最优方案。

App额外注意事项

  • App使用自定义组件模式,Android会增加6M左右的包体积。原因是App端增加了一个独立的v8以减少js的阻塞。iOS不变化,因为iOS的jscore是iOS自带的。
  • 离线打包的项目需要在原生工程里引入 :离线sdk包里的liblibWeex.a库 和 weex-main-jsfm.js 和 weex-polyfill.js文件。
  • 在自定义组件编译模式下,HBuilderX 2.4.0+ 支持 crypto.getRandomValues 方法,一些区块链应用的框架会用到此api
  • 在自定义组件编译模式下,uni.request 和 websocket 暂时不支持传输 ArrayBuffer 类型的数据,且 websocket 只能创建一个连接,有需求的用户可改用老的模板编译模式。
收起阅读 »

React Native基础+进阶+高级+实战视频教程

React Native

分享一套React Native基础+进阶+高级+实战视频教程,React Native零基础到入门实战,IOS+安卓移动应用开发,贯穿全栈React Native开发App 实战教程,比较全的React Native教学课程

教程目录列表:

教程下载:https://www.sucaihuo.com/video/398.html

继续阅读 »

分享一套React Native基础+进阶+高级+实战视频教程,React Native零基础到入门实战,IOS+安卓移动应用开发,贯穿全栈React Native开发App 实战教程,比较全的React Native教学课程

教程目录列表:

教程下载:https://www.sucaihuo.com/video/398.html

收起阅读 »

数据可视化之下发图实践

作者:个推前端工程师 东风

随着互联网的快速发展,数据维度越来越广,呈现形式也越发丰富,具有多维度数据特点的相关业务实践都能通过可视化图表来展示,比如个推的下发图,从时间和区域两个维度,可以即时、直观地展现个推数据下发的过程。

一、下发图的由来
个推下发图主要用于呈现个推为APP提供推送服务时数据的下发过程,可以直观显示个推推送触达到的城市,有利于开发者对下发数据进行分析。

个推下发图运用了迁徙图的原理,再通过自主设计开发出的一套可视化展示图像。这一类型的可视化可以广泛应用于拥有地理位置信息和数据转移特征的数据展示。

二、下发图的构成
下发图主要由地图、地理位置信息,以及飞线组成。如下图所示:

三、下发图的技术要点

1.地图
地图可以利用第三方地图服务,也可以自主绘制地图,本文以后者为例。自主绘制的地图主要利用了墨卡托投影原理,将地球正轴圆柱投影,由经纬度信息转化到画布上对应的位置。

本文案例中用了 d3.js 中的 geoMercator 进行墨卡托投影转换。

然后我们可以在阿里云的 datav 中获取地图的 geojson 数据,具体地址可参见括号内链接,(https://datav.aliyun.com/tools/atlas )再通过 canvas 原生 Api,添加背景色、边框等,就可以画出想要的地图了。

注意:下图中的地图角度透视主要应用了 css 中的 transform,perspective、rotateX、rotateY、rotateZ 等。

遵循上述步骤,一个透视角度的静态地图就绘制完成了。

2.贝塞尔曲线
贝塞尔曲线是计算机图形学中相当重要的参数曲线,它通过一个方程来描述一条曲线,根据方程的最高阶数,又分为线性贝塞尔曲线、二次贝塞尔曲线、三次贝塞尔曲线和更高阶的贝塞尔曲线。

本案例中主要应用了二次贝塞尔曲线,二次贝塞尔曲线的函数如下:
B(t) = (1-t) ²P0 + 2t(1-t)P1 + t²P2, t ∈ [0,1]

上图为本文案例中飞线的贝塞尔曲线应用,其中 from 为起点,to 为终点,curveness 为曲线的曲率,取值-1 ~ 1,曲率的绝对值越大,曲线越弯曲,percent为飞线位置占比。

3.动画
在 canvas 中,动画效果的实现通常是由 window.requestAnimationFrame 循环执行,因此,飞线需要算出每一帧中飞线的状态,以及飞线的入场和离场形态。

4.发光效果
那么下发图的特效具体如何实现呢?首先我们来介绍一下头部发光效果的实现过程:

我们以工业中的HSL色彩模式为颜色标准,通过对色相(H)、饱和度(S)、明度(L)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色,HSL即代表色相、饱和度、明度三个通道的颜色,这一标准几乎包括了人类视力所能感知的所有颜色。以此为依据,可以发现光源的发光规律,即光源中心的明亮度最高,由内向外,明亮度依次递减。

所以根据配置的基础颜色,就能获取到顶点发光处颜色的明亮度,大致方法如下:

完成头部发光步骤后,接下来需要打造一个酷炫的形状。发光的头部是一个类似棉签棒的形状,该形状可以用一个半圆和一个三角形来绘制,再根据曲线的切线,获取三角形以及半圆的旋转角度。

完成下发图头部制作后,接下来需要进行尾部的操作,因为canvas自带线性渐变,所以具体代码如下:

canvas 的落地效果呈圆形渐变样式,当飞线到达终点后,完整的落地效果就开始展示,整个画面类似雨滴降落到地面。

5.透视
如果不调整透视角度,贝塞尔曲线的样式如下图所示:

当曲线与下发方向的角度呈90度时,曲率最大;角度为0度或者180度时,曲率最小,与余弦定律相似。

其中 from 是起始位置,to 是终止位置,curveness 是曲线的曲率,angel 是视线的角度。

最终效果如下:

四、技术选型
在进行下发图的技术选型时,个推技术团队对比了 svg 和 canvas 两种技术栈,最后选择了 canvas,然后配合 requestAnimationFrame 画出下发轨迹的帧动画。两款技术栈的具体性能对比如下:

五、总结
随着数据维度的扩展和丰富,数据可视化的形态日渐丰富。作为地理位置信息和数据转移特征的数据可视化图表,下发图可以更直观地展现个推为APP提供推送服务时的下发量、下发区域等数据,对APP的行业分析以及战略调整有着指导性意义。

继续阅读 »

作者:个推前端工程师 东风

随着互联网的快速发展,数据维度越来越广,呈现形式也越发丰富,具有多维度数据特点的相关业务实践都能通过可视化图表来展示,比如个推的下发图,从时间和区域两个维度,可以即时、直观地展现个推数据下发的过程。

一、下发图的由来
个推下发图主要用于呈现个推为APP提供推送服务时数据的下发过程,可以直观显示个推推送触达到的城市,有利于开发者对下发数据进行分析。

个推下发图运用了迁徙图的原理,再通过自主设计开发出的一套可视化展示图像。这一类型的可视化可以广泛应用于拥有地理位置信息和数据转移特征的数据展示。

二、下发图的构成
下发图主要由地图、地理位置信息,以及飞线组成。如下图所示:

三、下发图的技术要点

1.地图
地图可以利用第三方地图服务,也可以自主绘制地图,本文以后者为例。自主绘制的地图主要利用了墨卡托投影原理,将地球正轴圆柱投影,由经纬度信息转化到画布上对应的位置。

本文案例中用了 d3.js 中的 geoMercator 进行墨卡托投影转换。

然后我们可以在阿里云的 datav 中获取地图的 geojson 数据,具体地址可参见括号内链接,(https://datav.aliyun.com/tools/atlas )再通过 canvas 原生 Api,添加背景色、边框等,就可以画出想要的地图了。

注意:下图中的地图角度透视主要应用了 css 中的 transform,perspective、rotateX、rotateY、rotateZ 等。

遵循上述步骤,一个透视角度的静态地图就绘制完成了。

2.贝塞尔曲线
贝塞尔曲线是计算机图形学中相当重要的参数曲线,它通过一个方程来描述一条曲线,根据方程的最高阶数,又分为线性贝塞尔曲线、二次贝塞尔曲线、三次贝塞尔曲线和更高阶的贝塞尔曲线。

本案例中主要应用了二次贝塞尔曲线,二次贝塞尔曲线的函数如下:
B(t) = (1-t) ²P0 + 2t(1-t)P1 + t²P2, t ∈ [0,1]

上图为本文案例中飞线的贝塞尔曲线应用,其中 from 为起点,to 为终点,curveness 为曲线的曲率,取值-1 ~ 1,曲率的绝对值越大,曲线越弯曲,percent为飞线位置占比。

3.动画
在 canvas 中,动画效果的实现通常是由 window.requestAnimationFrame 循环执行,因此,飞线需要算出每一帧中飞线的状态,以及飞线的入场和离场形态。

4.发光效果
那么下发图的特效具体如何实现呢?首先我们来介绍一下头部发光效果的实现过程:

我们以工业中的HSL色彩模式为颜色标准,通过对色相(H)、饱和度(S)、明度(L)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色,HSL即代表色相、饱和度、明度三个通道的颜色,这一标准几乎包括了人类视力所能感知的所有颜色。以此为依据,可以发现光源的发光规律,即光源中心的明亮度最高,由内向外,明亮度依次递减。

所以根据配置的基础颜色,就能获取到顶点发光处颜色的明亮度,大致方法如下:

完成头部发光步骤后,接下来需要打造一个酷炫的形状。发光的头部是一个类似棉签棒的形状,该形状可以用一个半圆和一个三角形来绘制,再根据曲线的切线,获取三角形以及半圆的旋转角度。

完成下发图头部制作后,接下来需要进行尾部的操作,因为canvas自带线性渐变,所以具体代码如下:

canvas 的落地效果呈圆形渐变样式,当飞线到达终点后,完整的落地效果就开始展示,整个画面类似雨滴降落到地面。

5.透视
如果不调整透视角度,贝塞尔曲线的样式如下图所示:

当曲线与下发方向的角度呈90度时,曲率最大;角度为0度或者180度时,曲率最小,与余弦定律相似。

其中 from 是起始位置,to 是终止位置,curveness 是曲线的曲率,angel 是视线的角度。

最终效果如下:

四、技术选型
在进行下发图的技术选型时,个推技术团队对比了 svg 和 canvas 两种技术栈,最后选择了 canvas,然后配合 requestAnimationFrame 画出下发轨迹的帧动画。两款技术栈的具体性能对比如下:

五、总结
随着数据维度的扩展和丰富,数据可视化的形态日渐丰富。作为地理位置信息和数据转移特征的数据可视化图表,下发图可以更直观地展现个推为APP提供推送服务时的下发量、下发区域等数据,对APP的行业分析以及战略调整有着指导性意义。

收起阅读 »

最大资源网列表及剧集数据采集

zuidazy_list(page){
var pattern = new RegExp('-' + "(.?)" + '.html' +"."+ '_blank">' + "(.?)" + '</a' +"."+ '<span class="xing_vb5">' + "(.?)" + '</span>' +"."+ '<span class="xing_vb(6|7)">' + "(.?)" + '</span>',"g");
let data=[];
uni.request({
method:"GET",
url:'http://zuidazy.net/?m='+page+'.html',
success: (result) => {
if(result.statusCode==200){
// var data = result.data.match(/\/>(.
)\$(.*)<\/li>/g)
while (pattern.exec(result.data) != null){

                        data.push({"id":RegExp.$1,  
                                    "title":RegExp.$2,  
                                    "type":RegExp.$3,  
                                    "times":RegExp.$5});  
                        this.zuidazy_info(RegExp.$1)      
                        // result.push(RegExp.$1);  
                    }  

                }  

            },  
            fail: (e) => {  
                console.log(JSON.stringify(e))  
            }  
        });  
        console.log(data)  
    },  
    zuidazy_info(id){  
        let d1=[];  
        let d2=[];  
        let d3=[];  
        uni.request({  
            method:"GET",  
            url:'http://zuidazy.net/?m='+id+'.html',  
            success: (result) => {  
                if(result.statusCode==200){  
                    var data = result.data.match(/\/>(.*)\$(.*)<\/li>/g)  
                    for(var i=0;i<data.length;i++){  
                        if(data[i].indexOf('.m3u8')>-1){  
                            d1.push(this.getdata(data[i]))  
                        }else if(data[i].indexOf('.mp4')>-1){  
                            d2.push(this.getdata(data[i]))  
                        }else{  
                            d3.push(this.getdata(data[i]))  
                        }  
                    }  

                }  

            },  
            fail: (e) => {  
                console.log(JSON.stringify(e))  
            }  
        });  

        console.log([d1,d2,d3])  
    },  

继续阅读 »

zuidazy_list(page){
var pattern = new RegExp('-' + "(.?)" + '.html' +"."+ '_blank">' + "(.?)" + '</a' +"."+ '<span class="xing_vb5">' + "(.?)" + '</span>' +"."+ '<span class="xing_vb(6|7)">' + "(.?)" + '</span>',"g");
let data=[];
uni.request({
method:"GET",
url:'http://zuidazy.net/?m='+page+'.html',
success: (result) => {
if(result.statusCode==200){
// var data = result.data.match(/\/>(.
)\$(.*)<\/li>/g)
while (pattern.exec(result.data) != null){

                        data.push({"id":RegExp.$1,  
                                    "title":RegExp.$2,  
                                    "type":RegExp.$3,  
                                    "times":RegExp.$5});  
                        this.zuidazy_info(RegExp.$1)      
                        // result.push(RegExp.$1);  
                    }  

                }  

            },  
            fail: (e) => {  
                console.log(JSON.stringify(e))  
            }  
        });  
        console.log(data)  
    },  
    zuidazy_info(id){  
        let d1=[];  
        let d2=[];  
        let d3=[];  
        uni.request({  
            method:"GET",  
            url:'http://zuidazy.net/?m='+id+'.html',  
            success: (result) => {  
                if(result.statusCode==200){  
                    var data = result.data.match(/\/>(.*)\$(.*)<\/li>/g)  
                    for(var i=0;i<data.length;i++){  
                        if(data[i].indexOf('.m3u8')>-1){  
                            d1.push(this.getdata(data[i]))  
                        }else if(data[i].indexOf('.mp4')>-1){  
                            d2.push(this.getdata(data[i]))  
                        }else{  
                            d3.push(this.getdata(data[i]))  
                        }  
                    }  

                }  

            },  
            fail: (e) => {  
                console.log(JSON.stringify(e))  
            }  
        });  

        console.log([d1,d2,d3])  
    },  

收起阅读 »

uni-app加载外网地址会全屏把手机状态栏也覆盖住了

uniapp 5+App开发


场景是这样的:我们以前使用mui和5+开发过的项目已经部署在微信端可以外网访问的。现在想通过uni-app里面的web-view组件加载以前的微信端页面地址。但是在加载页面成功后,点击页面跳转重新打开页面会把手机状态栏覆盖住。

继续阅读 »


场景是这样的:我们以前使用mui和5+开发过的项目已经部署在微信端可以外网访问的。现在想通过uni-app里面的web-view组件加载以前的微信端页面地址。但是在加载页面成功后,点击页面跳转重新打开页面会把手机状态栏覆盖住。

收起阅读 »