HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

坐标天津,招聘优秀前端、uni-app开发人员

Vue uniapp 招聘

坐标天津,招聘优秀前端、uni-app开发人员
有意向者发送简历至496367142@qq.com

坐标天津,招聘优秀前端、uni-app开发人员
有意向者发送简历至496367142@qq.com

ios端地图组件咋这么坑爹

  1. 动态插入markers报错TypeError: JSON.stringify cannot serialize cyclic structures.
  2. controltap controls点击事件, ios端与小程序端取值不同, controlId iOS 端取e.detail.controlId,小程序端 e.controlId
  3. ios端地图组件用id类名设置样式不起作用

其他还有好多属性不兼容

继续阅读 »
  1. 动态插入markers报错TypeError: JSON.stringify cannot serialize cyclic structures.
  2. controltap controls点击事件, ios端与小程序端取值不同, controlId iOS 端取e.detail.controlId,小程序端 e.controlId
  3. ios端地图组件用id类名设置样式不起作用

其他还有好多属性不兼容

收起阅读 »

IM聊天教程:发送图片/视频/语音/表情

IM 即时通信 聊天 WEBSOCKET

经常有朋友问起,如何在IM即时通讯中实现发送图片、视频、语音和表情?

为此,小编特意写了一个vue版本的Demo,实现了图片视频文件和表情的的发送,参考这个Demo源代码,相信你就可以轻松的用Uniapp和小程序完成类似的功能。

演示Demo

源码地址:https://ext.dcloud.net.cn/plugin?id=5177

一、图片/视频/语音发送

对于语音、视频和图片的发送,您如果有注意的话,在使用QQ或者微信的时候,当有朋友发送图片和视频给您时,收到后,需要等一会儿才能显示出来。就是因为在发送的时候,只发送了文件的路径,您收到后,需要加载才能显示出来。因为当前主流的IM包括微信,QQ等对于图片和视频的发送,通常的做法都是:

  1. 上传文件到文件服务器
  2. 推送文件路径
  3. 收到文件路径
  4. 加载文件

并不会通过网络直接传送源文件,因为对于大文件的传输,会影响消息的即时性。

对于文件的上传,您可以选择直接上传到您自己的服务器,也可以选择上传到各种云服务的对象存储服务,也就是OSS上。

参考源码:

DemoService.prototype.sendFileMessage = function (type,content) {  
    let uploadResult = restapi.uploadFile(content);  
    let message = new Message(type, uploadResult.url);  
    uploadResult.promise.then(() => {  
        this.publish(message);  
    },() =>  {  
        var error = new Message(MessageType.TEXT, "文件上传失败.");  
        this.messages.unshift(error)  
    });  
    return uploadResult.promise;  
};

云服务的OSS具有更好的稳定性和高可用性,上传的速度也有保证,另外也可以和CDN配合,所以我们建议用GoEasy配合OSS服务来实现图片和视频的发送。

在本文的源码里,选择了使用阿里云的OSS作为文件上传服务器,您也可以切换为您自己实现的文件上传服务器,或者选择其他云服务的OSS,原理都是一样的。

二、发送表情

表情的发送也是非常简单的,只是对于一些第一次实现表情发送的同学来说,需要一个思路而已。

细心点的朋友,肯定有发现,当我们在QQ上聊天的时候,我们输入一个反斜杠+“cy”, 就像这样:/cy ,QQ就会立即显示为一个呲牙的表情,就像下图一样:

表情

哈哈哈,相信你已经心里已经明白了十之八九了,对吧?

没错,表情在发送的过程中其实就是发一个像“/cy”这样定义好的的字符串,在对方收到后“翻译”成表情而已。

那为什么不直接发图片,而要进行这么复杂的“翻译”呢?

因为字符串比图片更小,发送的速度更快,用户体验更好。一个系统中的用户成千上万,用字符串可以节约大量的带宽,节约系统资源。

原理讲明白了,我们就开始干活儿吧:

第一步、定义表情

定义一个key value的对象,key作为表情标签,value则为每个表情标签对应的图片:

let expressions = {  
    "[risus]": './images/risus.png',  
    "[kiss]": './images/kiss.png',  
    "[cry]": './images/cry.png',  
    "[die]": './images/die.png',  
    "[anger]": './images/anger.png',  
}

然后画一个表情选择的界面:

表情列表

第二步、选择表情

为每个图片的onclick事件中传入这个表情的字符串标签,当用户点击的时候,将表情的标签写入输入框,就成为了一个普通的字符串。在发送的时候,发送的其实就是这个表情的标签,也就是一个字符串。

<div class="goeasy-expression">  
            <div :class="[appearanceClass, 'goeasy-appearance']" @click="show = true">{{text}}</div>  
            <div class="expression-container" v-show="show">  
                <div class="expression-icon-content">  
                    <div class="expression-icon__item"  
                         v-for="expression in list"  
                         :key="expression.id"  
                         @click="selectExpression(expression)">  
                        <img :src="expressions[expression.tag]">  
                    </div>  

                </div>  
                <div class="close-expression" @click="show = false"></div>  
            </div>  
        </div>

第三步、收到表情和展示表情

当对方收到一个字符串后,跟第一步定义的key-value列表去匹配,如果能找到对应的表情,就在页面上展示对应的表情图片,如果找不到,就是一个普通的文本信息。

原理讲清楚了后,具体实现是不是很简单了?

参考我们提供的Demo源代码,相信你很快就能掌握实现方法。

Demo源码:https://gitee.com/goeasy-io/GoEasyDemo-vue-AudioPictureVideo

GoEasy系列教程:

继续阅读 »

经常有朋友问起,如何在IM即时通讯中实现发送图片、视频、语音和表情?

为此,小编特意写了一个vue版本的Demo,实现了图片视频文件和表情的的发送,参考这个Demo源代码,相信你就可以轻松的用Uniapp和小程序完成类似的功能。

演示Demo

源码地址:https://ext.dcloud.net.cn/plugin?id=5177

一、图片/视频/语音发送

对于语音、视频和图片的发送,您如果有注意的话,在使用QQ或者微信的时候,当有朋友发送图片和视频给您时,收到后,需要等一会儿才能显示出来。就是因为在发送的时候,只发送了文件的路径,您收到后,需要加载才能显示出来。因为当前主流的IM包括微信,QQ等对于图片和视频的发送,通常的做法都是:

  1. 上传文件到文件服务器
  2. 推送文件路径
  3. 收到文件路径
  4. 加载文件

并不会通过网络直接传送源文件,因为对于大文件的传输,会影响消息的即时性。

对于文件的上传,您可以选择直接上传到您自己的服务器,也可以选择上传到各种云服务的对象存储服务,也就是OSS上。

参考源码:

DemoService.prototype.sendFileMessage = function (type,content) {  
    let uploadResult = restapi.uploadFile(content);  
    let message = new Message(type, uploadResult.url);  
    uploadResult.promise.then(() => {  
        this.publish(message);  
    },() =>  {  
        var error = new Message(MessageType.TEXT, "文件上传失败.");  
        this.messages.unshift(error)  
    });  
    return uploadResult.promise;  
};

云服务的OSS具有更好的稳定性和高可用性,上传的速度也有保证,另外也可以和CDN配合,所以我们建议用GoEasy配合OSS服务来实现图片和视频的发送。

在本文的源码里,选择了使用阿里云的OSS作为文件上传服务器,您也可以切换为您自己实现的文件上传服务器,或者选择其他云服务的OSS,原理都是一样的。

二、发送表情

表情的发送也是非常简单的,只是对于一些第一次实现表情发送的同学来说,需要一个思路而已。

细心点的朋友,肯定有发现,当我们在QQ上聊天的时候,我们输入一个反斜杠+“cy”, 就像这样:/cy ,QQ就会立即显示为一个呲牙的表情,就像下图一样:

表情

哈哈哈,相信你已经心里已经明白了十之八九了,对吧?

没错,表情在发送的过程中其实就是发一个像“/cy”这样定义好的的字符串,在对方收到后“翻译”成表情而已。

那为什么不直接发图片,而要进行这么复杂的“翻译”呢?

因为字符串比图片更小,发送的速度更快,用户体验更好。一个系统中的用户成千上万,用字符串可以节约大量的带宽,节约系统资源。

原理讲明白了,我们就开始干活儿吧:

第一步、定义表情

定义一个key value的对象,key作为表情标签,value则为每个表情标签对应的图片:

let expressions = {  
    "[risus]": './images/risus.png',  
    "[kiss]": './images/kiss.png',  
    "[cry]": './images/cry.png',  
    "[die]": './images/die.png',  
    "[anger]": './images/anger.png',  
}

然后画一个表情选择的界面:

表情列表

第二步、选择表情

为每个图片的onclick事件中传入这个表情的字符串标签,当用户点击的时候,将表情的标签写入输入框,就成为了一个普通的字符串。在发送的时候,发送的其实就是这个表情的标签,也就是一个字符串。

<div class="goeasy-expression">  
            <div :class="[appearanceClass, 'goeasy-appearance']" @click="show = true">{{text}}</div>  
            <div class="expression-container" v-show="show">  
                <div class="expression-icon-content">  
                    <div class="expression-icon__item"  
                         v-for="expression in list"  
                         :key="expression.id"  
                         @click="selectExpression(expression)">  
                        <img :src="expressions[expression.tag]">  
                    </div>  

                </div>  
                <div class="close-expression" @click="show = false"></div>  
            </div>  
        </div>

第三步、收到表情和展示表情

当对方收到一个字符串后,跟第一步定义的key-value列表去匹配,如果能找到对应的表情,就在页面上展示对应的表情图片,如果找不到,就是一个普通的文本信息。

原理讲清楚了后,具体实现是不是很简单了?

参考我们提供的Demo源代码,相信你很快就能掌握实现方法。

Demo源码:https://gitee.com/goeasy-io/GoEasyDemo-vue-AudioPictureVideo

GoEasy系列教程:

收起阅读 »

V3 部分样式 异常

v3

V3下

我主要发现了

  1. uni-icons 的图标为 undefined
  2. uni-icons被父元素的样式所影响

对于第一个问题;我没去深究原因;直接改用了 iconfont,便有图标了

对于第二个问题

这主要是我写的样式 采用 <text>标签赋值,或许这样权重太高,样式穿透了。我用 class 样式后,页面就正常了

原写法(该写法V3 下样式异常)

总结下 还是对语法要求 高了些

  • 我把应用的首页改为了Nvue 页面,好像在现在这个版本,fast 启动模式 有效了。

  • 在上个版本,andriod 下 还会有一个 taskcenter error

示例代码

继续阅读 »

V3下

我主要发现了

  1. uni-icons 的图标为 undefined
  2. uni-icons被父元素的样式所影响

对于第一个问题;我没去深究原因;直接改用了 iconfont,便有图标了

对于第二个问题

这主要是我写的样式 采用 <text>标签赋值,或许这样权重太高,样式穿透了。我用 class 样式后,页面就正常了

原写法(该写法V3 下样式异常)

总结下 还是对语法要求 高了些

  • 我把应用的首页改为了Nvue 页面,好像在现在这个版本,fast 启动模式 有效了。

  • 在上个版本,andriod 下 还会有一个 taskcenter error

示例代码

收起阅读 »

个人接单插件开发(只接原生插件)。

插件开发

个人接单插件开发(只接原生插件,iOS、Android 双端)。如有需要的可以联系 QQ。176142998

个人接单插件开发(只接原生插件,iOS、Android 双端)。如有需要的可以联系 QQ。176142998

扬州招聘前端uni-app工程师

招聘

职位要求:
1.大学本科(全日制)及以上学历,软件技术、计算机科学与技术、电子工程等相关专业;
2.1-3年以上Web前端工作经验、对小程序前端开发有经验;
3.熟练使用JavaScript、CSS/CSS3.HTML4/HTML5编程开发,熟悉其标准规范及各浏览器差异性;
4.熟练掌握AjaxHTTP等基础知识了解跨域机制;
5.熟练前端模块化开发,面向对象编程开发;
6.熟练使用jQuery、Vue、uni-app、Ember等框架对前端组件化有一定的理解;
7.熟练使用前端代码管理及打包工具。

工作地点:扬州设计瑰谷
投递邮箱:huashi3483@qq.com

继续阅读 »

职位要求:
1.大学本科(全日制)及以上学历,软件技术、计算机科学与技术、电子工程等相关专业;
2.1-3年以上Web前端工作经验、对小程序前端开发有经验;
3.熟练使用JavaScript、CSS/CSS3.HTML4/HTML5编程开发,熟悉其标准规范及各浏览器差异性;
4.熟练掌握AjaxHTTP等基础知识了解跨域机制;
5.熟练前端模块化开发,面向对象编程开发;
6.熟练使用jQuery、Vue、uni-app、Ember等框架对前端组件化有一定的理解;
7.熟练使用前端代码管理及打包工具。

工作地点:扬州设计瑰谷
投递邮箱:huashi3483@qq.com

收起阅读 »

uni-app阻止事件向父级冒泡

在官网找到的就只有这个方法,但是我放在app项目里并不支持,所以就想到vue的阻止事件冒泡的方法,现在分享,免得大家踩坑
@click.stop.prevent="onClick" 事件名仅为示范

在官网找到的就只有这个方法,但是我放在app项目里并不支持,所以就想到vue的阻止事件冒泡的方法,现在分享,免得大家踩坑
@click.stop.prevent="onClick" 事件名仅为示范

chrome拓展(收藏uniapp-插件市场插件)

效果图:

使用方法:

1.打开Chrome浏览器,然后可以看到右上角有个三个点组成的图标

2.更多工具=>拓展程序=>打开开发者模式=>加载已解压的扩展程序=>选择该项目文件

3.刷新插件市场页面

github

继续阅读 »

效果图:

使用方法:

1.打开Chrome浏览器,然后可以看到右上角有个三个点组成的图标

2.更多工具=>拓展程序=>打开开发者模式=>加载已解压的扩展程序=>选择该项目文件

3.刷新插件市场页面

github

收起阅读 »

贡献native.js(NJS)一些代码,将持续更新

Native.JS NJS

===为了书写方便,同时为了性能,频繁使用importClass()导入类会损失更多性能,所以封装了导入类=====================
使用方法:
iosImport('Hello'); //默认导入windows全局变量
var hello = new Hello();
hello.say();

如果不想导入windows全局变量,则可以这样:
var Hello = iosImport('Hello', false); //多传入一个参数false
var hello = new Hello();
hello.say();

    var androidImported = {};  
    var iosImported = {};  

    window.androidImport = function(classname, global) {  
        if (false !== global) {  
            global = true;  
        }  

        if (!window.plus) {  
            return null;  
        }  

        if (!androidImported[classname]) {  
            androidImported[classname] = plus.android.importClass(classname);  
        }  

        if (global) {  
            var myClass = classname.split('.').pop();  
            if (!window[myClass]) {  
                window[myClass] = androidImported[classname];  
            }  
        }  

        return androidImported[classname];  
    }  

    window.iosImport = function(classname, global) {  
        if (false !== global) {  
            global = true;  
        }  

        if (!window.plus) {  
            return null;  
        }  

        if (!iosImported[classname]) {  
            iosImported[classname] = plus.ios.importClass(classname);  
        }  

        if (global) {  
            var myClass = classname.split('.').pop();  
            if (!window[myClass]) {  
                window[myClass] = iosImported[classname];  
            }  
        }  

        return iosImported[classname];  
    }

===播放自定义提示音(需离线打包,添加自己的声音资源)================================
搞这个是因为官方没提供播放系统声音的方法,而使用plus.ios.invoke又没效果,比如plus.ios.invoke(null, 'AudioServicesPlaySystemSound', 1103);压根不起作用,官方的plus.audio.createPlayer感觉又有点麻烦,也不知道能否播放自己添加的声音资源,还需要添加liblibMedia.a,libopencore-amrnb.a,libmp3lame.a这几个库文件,增加包尺寸。

使用方法:
iosPlaySystemSound('my.wav');

window.iosPlaySystemSound = function(filename) {  
        if (!window.plus) {  
            return false;  
        }  

        iosImport('NSBundle');  
        iosImport('AVAudioPlayer');  

        var bundle = NSBundle.mainBundle();  
        var soundUrl = bundle.URLForResourcewithExtension(filename, null);  

        var player = new AVAudioPlayer();  
        player.initWithContentsOfURLerror(soundUrl, null);  
        if (!player) {  
            console.log('failed to play: ' + filename);  
            return false;  
        }  
        player.setNumberOfLoops(0); //-1:无限循环  
        player.setVolume(1);  
        player.prepareToPlay();  
        player.play();  
    }

===短震,震动反馈==============================================

window.shortVibrate = function() {  
        if (!window.plus) {  
                return false;  
        }  

        if (plus.os.name == 'iOS') {  
            iosImport('UIImpactFeedbackGenerator');  
            var impact = new UIImpactFeedbackGenerator();  
            impact.prepare();  
            impact.init(1);  
            impact.impactOccurred();  
        } else {  
            window.plus.device.vibrate(10);  
        }  
    }
继续阅读 »

===为了书写方便,同时为了性能,频繁使用importClass()导入类会损失更多性能,所以封装了导入类=====================
使用方法:
iosImport('Hello'); //默认导入windows全局变量
var hello = new Hello();
hello.say();

如果不想导入windows全局变量,则可以这样:
var Hello = iosImport('Hello', false); //多传入一个参数false
var hello = new Hello();
hello.say();

    var androidImported = {};  
    var iosImported = {};  

    window.androidImport = function(classname, global) {  
        if (false !== global) {  
            global = true;  
        }  

        if (!window.plus) {  
            return null;  
        }  

        if (!androidImported[classname]) {  
            androidImported[classname] = plus.android.importClass(classname);  
        }  

        if (global) {  
            var myClass = classname.split('.').pop();  
            if (!window[myClass]) {  
                window[myClass] = androidImported[classname];  
            }  
        }  

        return androidImported[classname];  
    }  

    window.iosImport = function(classname, global) {  
        if (false !== global) {  
            global = true;  
        }  

        if (!window.plus) {  
            return null;  
        }  

        if (!iosImported[classname]) {  
            iosImported[classname] = plus.ios.importClass(classname);  
        }  

        if (global) {  
            var myClass = classname.split('.').pop();  
            if (!window[myClass]) {  
                window[myClass] = iosImported[classname];  
            }  
        }  

        return iosImported[classname];  
    }

===播放自定义提示音(需离线打包,添加自己的声音资源)================================
搞这个是因为官方没提供播放系统声音的方法,而使用plus.ios.invoke又没效果,比如plus.ios.invoke(null, 'AudioServicesPlaySystemSound', 1103);压根不起作用,官方的plus.audio.createPlayer感觉又有点麻烦,也不知道能否播放自己添加的声音资源,还需要添加liblibMedia.a,libopencore-amrnb.a,libmp3lame.a这几个库文件,增加包尺寸。

使用方法:
iosPlaySystemSound('my.wav');

window.iosPlaySystemSound = function(filename) {  
        if (!window.plus) {  
            return false;  
        }  

        iosImport('NSBundle');  
        iosImport('AVAudioPlayer');  

        var bundle = NSBundle.mainBundle();  
        var soundUrl = bundle.URLForResourcewithExtension(filename, null);  

        var player = new AVAudioPlayer();  
        player.initWithContentsOfURLerror(soundUrl, null);  
        if (!player) {  
            console.log('failed to play: ' + filename);  
            return false;  
        }  
        player.setNumberOfLoops(0); //-1:无限循环  
        player.setVolume(1);  
        player.prepareToPlay();  
        player.play();  
    }

===短震,震动反馈==============================================

window.shortVibrate = function() {  
        if (!window.plus) {  
                return false;  
        }  

        if (plus.os.name == 'iOS') {  
            iosImport('UIImpactFeedbackGenerator');  
            var impact = new UIImpactFeedbackGenerator();  
            impact.prepare();  
            impact.init(1);  
            impact.impactOccurred();  
        } else {  
            window.plus.device.vibrate(10);  
        }  
    }
收起阅读 »

团队全职长期接APP,中后台开发

外包

开发经验:5年。
UNI经验:从UNI发布至今一直使用,前以及开发了10多个UNIAPP项目
UNI项目其中包含:淘宝客项目、教育类项目、区块链项目、商城项目等其他小中大型APP

说明:团队承接,长期接外包,公司个人均可
技术方向:uniappvuepythonphpflutternodejs

联系方式q:1910563900
微信:BGF-FF

继续阅读 »

开发经验:5年。
UNI经验:从UNI发布至今一直使用,前以及开发了10多个UNIAPP项目
UNI项目其中包含:淘宝客项目、教育类项目、区块链项目、商城项目等其他小中大型APP

说明:团队承接,长期接外包,公司个人均可
技术方向:uniappvuepythonphpflutternodejs

联系方式q:1910563900
微信:BGF-FF

收起阅读 »

创造力是让人去“

作为决定创造范围的想像

作为决定创造范围的想像