HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

推荐小册课程《前端算法与数据结构面试》

小程序 HBuilder HTML5 uniapp

简明易懂的前端算法面试教学,接地气&说人话&大量图解,带你从编码基本功到手撕大厂真题、构建坚不可摧的算法能力体系。

前端算法与数据结构面试

简明易懂的前端算法面试教学,接地气&说人话&大量图解,带你从编码基本功到手撕大厂真题、构建坚不可摧的算法能力体系。

前端算法与数据结构面试

已存在待跳转页面...请不要连续多次跳转页面...

详细问题描述

页面在onShow中延迟启动扫码框,当延迟时间比较小的时候,bug出现:在开启扫码框后,点击返回,扫码框退出,返回到本页面,再次返回,返回主页面,在这时,无法再次进入页面。
但延迟时间大一点,这个问题就消失了。

async scanQrCode() {  
            let _this = this;  
            let status = await _this.checkPermission();  
            if (status !== 1) {  
                return;  
            }  
            uni.scanCode({  
                onlyFromCamera: true,  
                success: function(res) {  
                }  
            });  
        },  

onShow() {  
        //页面载入执行扫码  
        var _this = this;  
        setTimeout(function() {  
            _this.scanQrCode();  
        }, 300);  
    }
继续阅读 »

详细问题描述

页面在onShow中延迟启动扫码框,当延迟时间比较小的时候,bug出现:在开启扫码框后,点击返回,扫码框退出,返回到本页面,再次返回,返回主页面,在这时,无法再次进入页面。
但延迟时间大一点,这个问题就消失了。

async scanQrCode() {  
            let _this = this;  
            let status = await _this.checkPermission();  
            if (status !== 1) {  
                return;  
            }  
            uni.scanCode({  
                onlyFromCamera: true,  
                success: function(res) {  
                }  
            });  
        },  

onShow() {  
        //页面载入执行扫码  
        var _this = this;  
        setTimeout(function() {  
            _this.scanQrCode();  
        }, 300);  
    }
收起阅读 »

iOS上架被调查相关问题解答

iOS iOS打包

今年以来因为审核的加严,很多iOS开发者账号提交ipa上架审核,直接反馈账号被调查了,也没有具体APP原因。

反馈邮件翻译

Hello,

We are unable to continue this app’s review because your Apple Developer Program account is currently under investigation for not following the App Store Review Guidelines’ Developer Code of Conduct.

Common practices that may lead to an investigation include, but are not limited to:

你好

我们无法继续此应用的审核,因为您的Apple开发者计划帐户目前正在接受调查,因为其未遵循App Store审核指南的“开发人员行为准则”。

可能导致调查的常见做法包括但不限于:

1、 不准确地描述应用程序或服务

2、误导性应用内容

3、参与不真实的评级和评论操作

4、提供误导性的客户支持响应

5、在解决方案中心提供误导性回复

6、从事误导性采购或诱饵开关计划

7、在应用内部或外部参与其他不诚实或欺诈活动

在我们的调查过程中,我们不会审核您提交的任何应用。 完成调查后,我们将通过解决方案中心通知您。

我们目前不要求您提供任何其他信息,也不会分享任何其他详细信息。 我们感您在调查期间继续保持耐心。

最好的祝福

下面列举一些解答,了解具体怎么回事。

1、为什么会账号会调查?

答:可能提交的APP是代码相似,市场上有很多类似App,认为马甲包,或者APP本身有违规,也是因为苹果机器审核越来越严格的原因,比较容易触发!

2、账号调查了会持续多久?

答:时间不确定,被调查多数在一个月内出结果,也有几个月的。如果调查没问题,也可能直接给通过APP审核。

3、账号被调查了怎么办?

答:调查期间,不要操作,申诉和加急都无效,只能等待出结果。在被调查阶段,无论如何提交都是统一回复的,回复你账号被调查,一直到出现2个结果,账号调查才结束。

1)直接过审;

2)被拒绝,有明确拒绝条款;

4.一个App被调查了,会影响同账号下的其他App吗?

答:被调查邮件里虽然说不会审核,但是其实是针对这一个App的,其他 App 项目还是可以提交正常审核了。同一个APP就不要换账号,或者删除再重新提交审核了,等调查结束在操作,调查完可能直接给通过。

5.把被调查的App转移到另一个开发者账号重新提交,会有问题吗?

答:因为是针对App的调查,所以转移App大概率无效,并且可能会被判定重复提交。另外,不建议把正在审核的App删除,很有可能被苹果认为是恶意隐瞒,直接导致封号。

6.此次调查是机审还是人审?

答:基本确定是机审,且是查重审核。因为从整个事件来看,是在清理灰色地带的应用。可能你的各个App的功能完全不同,但很多基础功能都是复用统一代码模块,这样确实会节省很多成本,但现在看很大概率会被误判为重复应用。

  1. 正常通过调查之后,App会有啥影响,会被列为重点审核对象吗?

答:目前来看没啥影响,甚至可能已被标记为安全对象,审核异常顺利。

iOS上架交流社区

http://www.applicationloader.net/

继续阅读 »

今年以来因为审核的加严,很多iOS开发者账号提交ipa上架审核,直接反馈账号被调查了,也没有具体APP原因。

反馈邮件翻译

Hello,

We are unable to continue this app’s review because your Apple Developer Program account is currently under investigation for not following the App Store Review Guidelines’ Developer Code of Conduct.

Common practices that may lead to an investigation include, but are not limited to:

你好

我们无法继续此应用的审核,因为您的Apple开发者计划帐户目前正在接受调查,因为其未遵循App Store审核指南的“开发人员行为准则”。

可能导致调查的常见做法包括但不限于:

1、 不准确地描述应用程序或服务

2、误导性应用内容

3、参与不真实的评级和评论操作

4、提供误导性的客户支持响应

5、在解决方案中心提供误导性回复

6、从事误导性采购或诱饵开关计划

7、在应用内部或外部参与其他不诚实或欺诈活动

在我们的调查过程中,我们不会审核您提交的任何应用。 完成调查后,我们将通过解决方案中心通知您。

我们目前不要求您提供任何其他信息,也不会分享任何其他详细信息。 我们感您在调查期间继续保持耐心。

最好的祝福

下面列举一些解答,了解具体怎么回事。

1、为什么会账号会调查?

答:可能提交的APP是代码相似,市场上有很多类似App,认为马甲包,或者APP本身有违规,也是因为苹果机器审核越来越严格的原因,比较容易触发!

2、账号调查了会持续多久?

答:时间不确定,被调查多数在一个月内出结果,也有几个月的。如果调查没问题,也可能直接给通过APP审核。

3、账号被调查了怎么办?

答:调查期间,不要操作,申诉和加急都无效,只能等待出结果。在被调查阶段,无论如何提交都是统一回复的,回复你账号被调查,一直到出现2个结果,账号调查才结束。

1)直接过审;

2)被拒绝,有明确拒绝条款;

4.一个App被调查了,会影响同账号下的其他App吗?

答:被调查邮件里虽然说不会审核,但是其实是针对这一个App的,其他 App 项目还是可以提交正常审核了。同一个APP就不要换账号,或者删除再重新提交审核了,等调查结束在操作,调查完可能直接给通过。

5.把被调查的App转移到另一个开发者账号重新提交,会有问题吗?

答:因为是针对App的调查,所以转移App大概率无效,并且可能会被判定重复提交。另外,不建议把正在审核的App删除,很有可能被苹果认为是恶意隐瞒,直接导致封号。

6.此次调查是机审还是人审?

答:基本确定是机审,且是查重审核。因为从整个事件来看,是在清理灰色地带的应用。可能你的各个App的功能完全不同,但很多基础功能都是复用统一代码模块,这样确实会节省很多成本,但现在看很大概率会被误判为重复应用。

  1. 正常通过调查之后,App会有啥影响,会被列为重点审核对象吗?

答:目前来看没啥影响,甚至可能已被标记为安全对象,审核异常顺利。

iOS上架交流社区

http://www.applicationloader.net/

收起阅读 »

uni-app友盟和官方离线多渠道打包

uniapp离线打包 uni_app uniapp 教程 uni统计

公司要求用友盟但是官方不支持友盟多渠道打包,所以必须一个一个打,但是官方又有打包限制而且速度很慢,所以想到了利用反编译重新打包
实现原理:apkTool反编译资源>修改AndroidManifest.xml>重新打包>应用签名
适用平台:Window、依赖JDK
操作步骤:解压压缩包script.vbs、apktool_2.4.1.jar、apktool.bat三个文件
1、将你生成的apk文件放入解呀目录与上述三个文件同目录
2、用记事本打开script.vbs

channelList = array("oppo","baidu","huawei","xiaomi") //渠道列表可以自己按需求添加或者移除  
apkName = "app" //修改app为你apk对应的文件名(不带.apk后缀),如app.apk则填写app  
keystore = "D:\\test.keystore" //修改为你签名文件路径  
keystorePwd = "12345678" //签名文件密码  
keystoreAlias = "testalias" //签名文件别名

3、保存,双击script.vbs
4、等待生成app_oppo.apk等文件则结束
注意事项:在执行后会产生apk对应的文件夹,如果apk发生变化要删除此文件夹不然打包是之前的
链接: https://pan.baidu.com/s/1s6Z4TNlW0vhn1PXq8xk5kw提取码: kf2f

继续阅读 »

公司要求用友盟但是官方不支持友盟多渠道打包,所以必须一个一个打,但是官方又有打包限制而且速度很慢,所以想到了利用反编译重新打包
实现原理:apkTool反编译资源>修改AndroidManifest.xml>重新打包>应用签名
适用平台:Window、依赖JDK
操作步骤:解压压缩包script.vbs、apktool_2.4.1.jar、apktool.bat三个文件
1、将你生成的apk文件放入解呀目录与上述三个文件同目录
2、用记事本打开script.vbs

channelList = array("oppo","baidu","huawei","xiaomi") //渠道列表可以自己按需求添加或者移除  
apkName = "app" //修改app为你apk对应的文件名(不带.apk后缀),如app.apk则填写app  
keystore = "D:\\test.keystore" //修改为你签名文件路径  
keystorePwd = "12345678" //签名文件密码  
keystoreAlias = "testalias" //签名文件别名

3、保存,双击script.vbs
4、等待生成app_oppo.apk等文件则结束
注意事项:在执行后会产生apk对应的文件夹,如果apk发生变化要删除此文件夹不然打包是之前的
链接: https://pan.baidu.com/s/1s6Z4TNlW0vhn1PXq8xk5kw提取码: kf2f

收起阅读 »

图片压缩及转换base64

uni_app 5+App开发 base64

本教程适合APP,利用5+进行图片压缩,然后转换成base64格式文件。

uni.chooseImage({  
    sizeType: ['original'],  
    sourceType: ['camera'],  
    count: 1,  
    success: async (res) => {  
            // 加载框  
            uni.showLoading({  
                title: '图片压缩中。。。'  
            });  
            let path = res.tempFilePaths[0];  
            // 压缩图片  
            plus.zip.compressImage({    
                src:path,    
                dst:path,    
                overwrite:true,    
                quality:20,    
                width: '780px',      
                height:'1040px',      
                format: 'jpg'    
                },    
                function(res) {    
                    console.log("图片压缩完成");    
                    let imgPathUrl = res.target;   
                    let imgPathSize = res.size;  
                    console.log("图片链接:"+imgPathUrl)  
                    console.log("图片尺寸:"+imgPathSize)  
                    // 文件系统中的读取文件对象,用于获取文件的内容  
                    uni.showLoading({  
                        title: '图片转换中。。。'  
                    });  
                    let reader = new plus.io.FileReader();   
                    // 文件读取操作完成时的回调函数  
                    reader.onloadend = (fileData)=> {  
                        uni.hideLoading();  
                        console.log('文件读取完成!');      
                        let speech = fileData.target.result;//base64图片   
                        // 去除base64文件头  
                        let imgData = speech.replace(/^data:image\/\w+;base64,/, "");  

                    };      
                    reader.readAsDataURL(res.target);  
                },    
                function(error) {    
                    console.log("Compress error!",error);   
                    return;   

            });  
        },  
        fail:(err) => {  
            // 加载框  
            console.log(err)  
            uni.showToast({  
                icon: 'none',  
                duration:3000,  
                title: '拍照失败'  
            });  
        }  
    });
继续阅读 »

本教程适合APP,利用5+进行图片压缩,然后转换成base64格式文件。

uni.chooseImage({  
    sizeType: ['original'],  
    sourceType: ['camera'],  
    count: 1,  
    success: async (res) => {  
            // 加载框  
            uni.showLoading({  
                title: '图片压缩中。。。'  
            });  
            let path = res.tempFilePaths[0];  
            // 压缩图片  
            plus.zip.compressImage({    
                src:path,    
                dst:path,    
                overwrite:true,    
                quality:20,    
                width: '780px',      
                height:'1040px',      
                format: 'jpg'    
                },    
                function(res) {    
                    console.log("图片压缩完成");    
                    let imgPathUrl = res.target;   
                    let imgPathSize = res.size;  
                    console.log("图片链接:"+imgPathUrl)  
                    console.log("图片尺寸:"+imgPathSize)  
                    // 文件系统中的读取文件对象,用于获取文件的内容  
                    uni.showLoading({  
                        title: '图片转换中。。。'  
                    });  
                    let reader = new plus.io.FileReader();   
                    // 文件读取操作完成时的回调函数  
                    reader.onloadend = (fileData)=> {  
                        uni.hideLoading();  
                        console.log('文件读取完成!');      
                        let speech = fileData.target.result;//base64图片   
                        // 去除base64文件头  
                        let imgData = speech.replace(/^data:image\/\w+;base64,/, "");  

                    };      
                    reader.readAsDataURL(res.target);  
                },    
                function(error) {    
                    console.log("Compress error!",error);   
                    return;   

            });  
        },  
        fail:(err) => {  
            // 加载框  
            console.log(err)  
            uni.showToast({  
                icon: 'none',  
                duration:3000,  
                title: '拍照失败'  
            });  
        }  
    });
收起阅读 »

uniapp是准备走向哪里

吐槽一下,最近打算用uniapp开发一个Android pad + window(NWjs)的项目。
在开发过程中,遇到的坑不要太多了,感觉官方仅考虑支持手机+小程序,发现问题如下:

  1. 官方组件:input、button等多端不一致:mobile、pad、h5
  2. 编译环境问题频繁:没有提示任何错误,但Android端调试模式下,pages中配置的页面加载不了,没做任何修改,突然又可以了。
  3. dev模式下,访问 局域网地址(编译好后,会提供本地+局域网两个地址),该地址下的sockjs-node 访问的地址确是 localhost,意味着如果你是一台开发,一台预览,那么恩,热更新无效了
    我的建议
  • 丰富生态:插件市场中需要有人去跟进维护
  • 插件质量:优化现有组件,提高多端一致性(毕竟也会有人开发pad版本的),这部分甚至可以外包给第三方团队。
  • 专注于底层环境,放弃hbuilderX或若降低hbuilderX在公司的比重,加强cli模式下的开发体验,丰富cli模式下的更多的配置指导、说明文档,以便提供更多的灵活性。
继续阅读 »

吐槽一下,最近打算用uniapp开发一个Android pad + window(NWjs)的项目。
在开发过程中,遇到的坑不要太多了,感觉官方仅考虑支持手机+小程序,发现问题如下:

  1. 官方组件:input、button等多端不一致:mobile、pad、h5
  2. 编译环境问题频繁:没有提示任何错误,但Android端调试模式下,pages中配置的页面加载不了,没做任何修改,突然又可以了。
  3. dev模式下,访问 局域网地址(编译好后,会提供本地+局域网两个地址),该地址下的sockjs-node 访问的地址确是 localhost,意味着如果你是一台开发,一台预览,那么恩,热更新无效了
    我的建议
  • 丰富生态:插件市场中需要有人去跟进维护
  • 插件质量:优化现有组件,提高多端一致性(毕竟也会有人开发pad版本的),这部分甚至可以外包给第三方团队。
  • 专注于底层环境,放弃hbuilderX或若降低hbuilderX在公司的比重,加强cli模式下的开发体验,丰富cli模式下的更多的配置指导、说明文档,以便提供更多的灵活性。
收起阅读 »

承接uni-app开发、后端PHP开发

外包

PHP开发经验:6年
uni-app经验:2年
uni-app项目包括:搞笑社区、资讯、工具、商城、教育

说明:全职开发,长期接外包,速度与质量有保证

联系方式
微信:shalibaji_ge

PHP开发经验:6年
uni-app经验:2年
uni-app项目包括:搞笑社区、资讯、工具、商城、教育

说明:全职开发,长期接外包,速度与质量有保证

联系方式
微信:shalibaji_ge

2.6.11 V 3

更新到2.6.11, 会暴露很多以前没有的问题, 是不是规范语法要求了!!!! 不知道是新版本和v3编译器哪个的问题,降了版本2.6.9还是会有错 报错: [-1]$r[4][t0-0] is undefined at view.umd.min.js:1; [-1]$r[8][t0-0] is undefined at view.umd.min.js:1; 之前可以是undefined和null的地方 现在不可以; TypeError: undefined is not an object (evaluating ; 项目升级后用2.6.11的版本打包后, 运行时好多样式都错乱了, 晚上检查到凌晨呀,各种改; 用老版本打包一点问题没有;

继续阅读 »

更新到2.6.11, 会暴露很多以前没有的问题, 是不是规范语法要求了!!!! 不知道是新版本和v3编译器哪个的问题,降了版本2.6.9还是会有错 报错: [-1]$r[4][t0-0] is undefined at view.umd.min.js:1; [-1]$r[8][t0-0] is undefined at view.umd.min.js:1; 之前可以是undefined和null的地方 现在不可以; TypeError: undefined is not an object (evaluating ; 项目升级后用2.6.11的版本打包后, 运行时好多样式都错乱了, 晚上检查到凌晨呀,各种改; 用老版本打包一点问题没有;

收起阅读 »

Websocket直播间聊天室教程 - GoEasy快速实现聊天室

直播 WEBSOCKET

最近两年直播那个火啊,真的是无法形容!经常有朋友问起,我想实现一个直播间聊天或者我想开发一个聊天室, 要如何开始呢?
image

今天小编就手把手的教你用GoEasy做一个聊天室,当然也可以用于直播间内的互动。全套源码已经开源。

uniapp websocket聊天室体验demo:https://ext.dcloud.net.cn/plugin?id=5212

本教程主要目的是为大家介绍实现思路,为了确保本教程能帮助到使用不同前端技术的朋友,采用了HTML + JQuery的方式,后续还会推出Uniapp(vue/nvue)和小程序版本,大家可以持续关注。

我们这次要实现的聊天室,有两个界面,分别是:

  • 登录界面
  • 聊天室界面

    登录

    image

对于登录界面,我们期望:

  • 用户可以输入自己的昵称
  • 用户可以选择自己喜欢的头像
  • 用户可以选择进入不同的聊天室(直播间)

实现步骤

登录界面的实现,不用多说,因为真的是So Easy! 一个简单的界面,只包含三个简单的逻辑:

  • 验证是否输入昵称
  • 验证是否选择一个头像
  • 根据选择进入相应的聊天室

下边重点讲一下聊天室的实现。

聊天室(直播间)

image

当我们进入一个聊天室后,我们期望:

  • 用户能看到当前有多少用户在线,这个数字能够实时的更新
  • 用户能看到当前在线用户们的头像,而且能够实时的更新
  • 如果有用户进入或离开聊天室
    a. 聊天室会有“XXX进来了"或"XXX离开了"的提示
    b. 在线用户的数字和用户的头像列表会随之自动更新
  • 用户可以在聊天里发言
  • 用户可以发送道具:火箭或者比心

实现步骤

第一步:聊天室界面显示

1. 初始化:

当用户选择了一个聊天室,显示聊天室界面之前,我们首先要进行以下初始化工作:

  • 初始化当前用户currentUser,用户id,昵称,头像
  • 初始化当前聊天室ID: currentRoomId
  • 初始化GoEasy对象,注意一定要加上userId参数(可以是该用户的uuid或id等唯一标识,只有设置了userId的客户端在上下线时,才会触发上下线提醒)。同时需要将头像和昵称放入userData,当我们收到一个用户上线提醒的时候,我们需要知道这个用户的头像和昵称。
  • 初始化onlineUsers,onlineUsers是用来存放当前聊天室在线用户数和在线用户列表。 将当前聊天室Id (currentRoomId)作为channel,执行goEasy.hereNow查询此刻聊天室在线用户数和用户列表,赋值给onlineUsers。除了在进入聊天室的时候初始化onlineUsers,当有用户进入或离开时,也会动态的更新onlineUsers。
  • 以当前聊天室的id(currentRoomId)作为channel,执行subscriber方法监听和接收聊天室新消息。
  • 以当前聊天室的id(currentRoomId)作为channel,执行subscriberPresence监听用户进入和离开事件。

参考代码:service.js

//初始化聊天室  
this.joinRoom = function(userId,nickName, avatar, roomID) {  
        //初始化当前用户  
        this.currentUser = new User(userId, nickName, avatar);  
        //初始化当前聊天室id  
        this.currentRoomId = roomID;  
        //初始化goeasy,建立长连接  
        this.goeasy = new GoEasy({  
            host: "hangzhou.goeasy.io",  
            appkey: "您的appkey",  
            userId: this.currentUser.id,  
            userData: '{"nickname":"' + this.currentUser.nickname + '","avatar":"' + this.currentUser.avatar + '"}',  
            onConnected: function () {  
                console.log( "GoEasy connect successfully.")  
            },  
            onDisconnected: function () {  
                console.log("GoEasy disconnected.")  
            }  
        });  
        //查询当前在线用户列表,初始化onlineUsers对象  
        this.initialOnlineUsers();  
        //监听用户上下线提醒,实时更新onlineUsers对象  
        this.subscriberPresence();  
        //监听和接收新消息  
        this.subscriberNewMessage();  
};

2. 页面展示:

完成初始化之后,就跳转到直播间界面,在页面上显示以下数据:

  • 当前聊天室的名称
  • 聊天记录,并且显示聊天室界面
  • 展示聊天室界面

参考代码:controller.js

//页面切换到聊天室界面  
function showChatRoom() {  
    //更新房间名  
    $("#chatRoom-header").find(".current-chatRoom-name").text(loginCommand.roomName);  

    //加载聊天历史  
    var chatHistory = service.loadChatHistory();  
    chatHistory.forEach(function (item) {  
        //展示发送的消息  
        var otherPerson = createCurrentChatRoomPerson(item.senderNickname + ":", item.content)  
        $(".chatRoom-content-box").append($(otherPerson));  
    });  

    //隐藏登录界面  
    $(".chat-login-box").hide();  
    // //显示聊天界面  
    $(".chatRoom-box").show();  
    // //滑动到最后一行  
    scrollBottom();  
}

至此,我们已经完成了goeasy长连接的初始化,和一个聊天室静态展示。接下来,我们一起来看看如何让这个聊天室能够动起来。

第二步:聊天室互动

1. 实时更新在线用户数和头像列表

之前在service.initialOnlineUsers方法已经初始化onlineUsers对象,但聊天室随时都有用户进进出出,所以我们接下来还需要能够在有用户上线或下线的时候能够实时的更新onlineUsers,并且实时显示在页面上。
当我们收到一个用户上线提醒,我们将新上线的用户的信息存入在线用户对象onlineUsers里,当有用户离开时,在本地在线用户列表里删除。

参考代码:service.js

//监听用户上下线时间,维护onlineUsers对象  
this.subscriberPresence = function() {  
    var self = this;  
    this.goeasy.subscribePresence({  
        channel: this.currentRoomId,  
        onPresence: function(presenceEvents) {  
            presenceEvents.events.forEach(function(event) {  
                var userId = event.userId;  
                var count = presenceEvents.clientAmount;  
                //更新onlineUsers在线用户数  
                self.onlineUsers.count = count;  
                //如果有用户进入聊天室  
                if (event.action == "join" || event.action == "online") {  
                    var userData = JSON.parse(event.userData);  
                    var nickName = userData.nickname;  
                    var avatar = userData.avatar;  
                    var user = new User(userId, nickName, avatar);  
                    //将新用户加入onlineUsers列表  
                    self.onlineUsers.users.push(user);  
                    //触发界面的更新  
                    self.onJoinRoom(user.nickname, user.avatar);  
                } else {  
                    for (var i = 0; i < self.onlineUsers.users.length; i++) {  
                        var leavingUser = self.onlineUsers.users[i];  
                        if (leavingUser.id == userId) {  
                            var nickName = leavingUser.nickname;  
                            var avatar = leavingUser.avatar;  
                            //将离开的用户从onlineUsers中删掉  
                            self.onlineUsers.users.splice(i, 1);  
                            //触发界面的更新  
                            self.onLeaveRoom(nickName, avatar);  
                        }  
                    }  
                }  
            });  
        },  
        onSuccess : function () {  
            console.log("监听成功")  
        },  
        onFailed : function () {  
            console.log("监听失败")  
        }  
    });  
};

2. 发送消息

  • 初始化一个chatMessage对象,包含发送方id,昵称,消息内容,消息类型为chat
  • 将chatMessage转换为一个Json格式的字符串
  • 调用GoEasy的Publish方法,完成消息的发送

参考代码(service.js)

this.sendMessage = function(content) {  
    var message = new ChatMessage(this.currentUser.id,this.currentUser.nickname, MessageType.CHAT, content);  
    var self = this;  
    this.goeasy.publish({  
        channel: self.currentRoomId,  
        message: JSON.stringify(message),  
        onSuccess: function() {  
            console.log("消息发布成功。");  
        },  
        onFailed: function(error) {  
            console.log("消息发送失败,错误编码:" + error.code + " 错误信息:" + error.content);  
        }  
    });  
};

3. 接收和显示新消息/道具

之前我们已经在初始化页面的时候执行了service.subscriberNewMessage(),当我们收到一条消息时:

  • 根据消息类型判断是一条聊天消息,还是一个道具
  • 如果收到的是一条聊天消息,直接显示到界面
  • 如果是道具,就播放动画

参考代码(service.js)

//监听消息或道具  
this.subscriberNewMessage = function() {  
    var self = this;  
    this.goeasy.subscribe({  
        channel: this.currentRoomId, //替换为您自己的channel  
        onMessage: function(message) {  
            var chatMessage = JSON.parse(message.content);  
            //todo:事实上不推荐在前端收到时保存, 一个用户开多个窗口,会导致重复保存, 建议所有消息都是都在发送时在服务器端保存,这里只是为了演示  
            self.restapi.saveChatMessage(self.currentRoomId, chatMessage);  
            //如果收到的是一个消息,就显示为消息  
            if (chatMessage.type == MessageType.CHAT) {  
                var selfSent = chatMessage.senderUserId == self.currentUser.id;  
                var content = JSON.parse(message.content);  
                self.onNewMessage(chatMessage.senderNickname, content, selfSent);  
            }  
            //如果收到的是一个道具,就播放道具动画  
            if (chatMessage.type == MessageType.PROP) {  
                if (chatMessage.content == Prop.ROCKET) {  
                    self.onNewRocket(chatMessage.senderNickname);  
                }  
                if (chatMessage.content == Prop.HEART) {  
                    self.onNewHeart(chatMessage.senderNickname);  
                }  
            }  
        }  
    });  
};

4. 发送和接收并展示道具

其实和发送消息的实现几乎是一样的,具体代码请参考service.js的sendProp方法,controller.js的onNewHeart()方法。动画的播放,使用了TweenMax这个库,主要是为了展示一个实现思路,小编也不知道这个库是否有很好的兼容性,以及是否能够用在Uniapp和小程序下,知道的朋友可以留言分享给大家。

this.sendProp = function(prop) {  
    var self = this;  
    var message = new ChatMessage(this.currentUser.id,this.currentUser.nickname, MessageType.PROP, prop);  
    this.goeasy.publish({  
        channel: self.currentRoomId,  
        message: JSON.stringify(message),  
        onSuccess: function() {  
            console.log("道具发布成功。");  
        },  
        onFailed: function(error) {  
            console.log("道具发送失败,错误编码:" + error.code + " 错误信息:" + error.content);  
        }  
    });  
};

至此,一个聊天室就搞定了,是不是很简单?

如果阅读本文或开发中有任何问题,也欢迎在GoEasy官网(https://www.goeasy.io)添加GoEasy为好友,来获得更多技术支持。

GoEasy系列教程:

继续阅读 »

最近两年直播那个火啊,真的是无法形容!经常有朋友问起,我想实现一个直播间聊天或者我想开发一个聊天室, 要如何开始呢?
image

今天小编就手把手的教你用GoEasy做一个聊天室,当然也可以用于直播间内的互动。全套源码已经开源。

uniapp websocket聊天室体验demo:https://ext.dcloud.net.cn/plugin?id=5212

本教程主要目的是为大家介绍实现思路,为了确保本教程能帮助到使用不同前端技术的朋友,采用了HTML + JQuery的方式,后续还会推出Uniapp(vue/nvue)和小程序版本,大家可以持续关注。

我们这次要实现的聊天室,有两个界面,分别是:

  • 登录界面
  • 聊天室界面

    登录

    image

对于登录界面,我们期望:

  • 用户可以输入自己的昵称
  • 用户可以选择自己喜欢的头像
  • 用户可以选择进入不同的聊天室(直播间)

实现步骤

登录界面的实现,不用多说,因为真的是So Easy! 一个简单的界面,只包含三个简单的逻辑:

  • 验证是否输入昵称
  • 验证是否选择一个头像
  • 根据选择进入相应的聊天室

下边重点讲一下聊天室的实现。

聊天室(直播间)

image

当我们进入一个聊天室后,我们期望:

  • 用户能看到当前有多少用户在线,这个数字能够实时的更新
  • 用户能看到当前在线用户们的头像,而且能够实时的更新
  • 如果有用户进入或离开聊天室
    a. 聊天室会有“XXX进来了"或"XXX离开了"的提示
    b. 在线用户的数字和用户的头像列表会随之自动更新
  • 用户可以在聊天里发言
  • 用户可以发送道具:火箭或者比心

实现步骤

第一步:聊天室界面显示

1. 初始化:

当用户选择了一个聊天室,显示聊天室界面之前,我们首先要进行以下初始化工作:

  • 初始化当前用户currentUser,用户id,昵称,头像
  • 初始化当前聊天室ID: currentRoomId
  • 初始化GoEasy对象,注意一定要加上userId参数(可以是该用户的uuid或id等唯一标识,只有设置了userId的客户端在上下线时,才会触发上下线提醒)。同时需要将头像和昵称放入userData,当我们收到一个用户上线提醒的时候,我们需要知道这个用户的头像和昵称。
  • 初始化onlineUsers,onlineUsers是用来存放当前聊天室在线用户数和在线用户列表。 将当前聊天室Id (currentRoomId)作为channel,执行goEasy.hereNow查询此刻聊天室在线用户数和用户列表,赋值给onlineUsers。除了在进入聊天室的时候初始化onlineUsers,当有用户进入或离开时,也会动态的更新onlineUsers。
  • 以当前聊天室的id(currentRoomId)作为channel,执行subscriber方法监听和接收聊天室新消息。
  • 以当前聊天室的id(currentRoomId)作为channel,执行subscriberPresence监听用户进入和离开事件。

参考代码:service.js

//初始化聊天室  
this.joinRoom = function(userId,nickName, avatar, roomID) {  
        //初始化当前用户  
        this.currentUser = new User(userId, nickName, avatar);  
        //初始化当前聊天室id  
        this.currentRoomId = roomID;  
        //初始化goeasy,建立长连接  
        this.goeasy = new GoEasy({  
            host: "hangzhou.goeasy.io",  
            appkey: "您的appkey",  
            userId: this.currentUser.id,  
            userData: '{"nickname":"' + this.currentUser.nickname + '","avatar":"' + this.currentUser.avatar + '"}',  
            onConnected: function () {  
                console.log( "GoEasy connect successfully.")  
            },  
            onDisconnected: function () {  
                console.log("GoEasy disconnected.")  
            }  
        });  
        //查询当前在线用户列表,初始化onlineUsers对象  
        this.initialOnlineUsers();  
        //监听用户上下线提醒,实时更新onlineUsers对象  
        this.subscriberPresence();  
        //监听和接收新消息  
        this.subscriberNewMessage();  
};

2. 页面展示:

完成初始化之后,就跳转到直播间界面,在页面上显示以下数据:

  • 当前聊天室的名称
  • 聊天记录,并且显示聊天室界面
  • 展示聊天室界面

参考代码:controller.js

//页面切换到聊天室界面  
function showChatRoom() {  
    //更新房间名  
    $("#chatRoom-header").find(".current-chatRoom-name").text(loginCommand.roomName);  

    //加载聊天历史  
    var chatHistory = service.loadChatHistory();  
    chatHistory.forEach(function (item) {  
        //展示发送的消息  
        var otherPerson = createCurrentChatRoomPerson(item.senderNickname + ":", item.content)  
        $(".chatRoom-content-box").append($(otherPerson));  
    });  

    //隐藏登录界面  
    $(".chat-login-box").hide();  
    // //显示聊天界面  
    $(".chatRoom-box").show();  
    // //滑动到最后一行  
    scrollBottom();  
}

至此,我们已经完成了goeasy长连接的初始化,和一个聊天室静态展示。接下来,我们一起来看看如何让这个聊天室能够动起来。

第二步:聊天室互动

1. 实时更新在线用户数和头像列表

之前在service.initialOnlineUsers方法已经初始化onlineUsers对象,但聊天室随时都有用户进进出出,所以我们接下来还需要能够在有用户上线或下线的时候能够实时的更新onlineUsers,并且实时显示在页面上。
当我们收到一个用户上线提醒,我们将新上线的用户的信息存入在线用户对象onlineUsers里,当有用户离开时,在本地在线用户列表里删除。

参考代码:service.js

//监听用户上下线时间,维护onlineUsers对象  
this.subscriberPresence = function() {  
    var self = this;  
    this.goeasy.subscribePresence({  
        channel: this.currentRoomId,  
        onPresence: function(presenceEvents) {  
            presenceEvents.events.forEach(function(event) {  
                var userId = event.userId;  
                var count = presenceEvents.clientAmount;  
                //更新onlineUsers在线用户数  
                self.onlineUsers.count = count;  
                //如果有用户进入聊天室  
                if (event.action == "join" || event.action == "online") {  
                    var userData = JSON.parse(event.userData);  
                    var nickName = userData.nickname;  
                    var avatar = userData.avatar;  
                    var user = new User(userId, nickName, avatar);  
                    //将新用户加入onlineUsers列表  
                    self.onlineUsers.users.push(user);  
                    //触发界面的更新  
                    self.onJoinRoom(user.nickname, user.avatar);  
                } else {  
                    for (var i = 0; i < self.onlineUsers.users.length; i++) {  
                        var leavingUser = self.onlineUsers.users[i];  
                        if (leavingUser.id == userId) {  
                            var nickName = leavingUser.nickname;  
                            var avatar = leavingUser.avatar;  
                            //将离开的用户从onlineUsers中删掉  
                            self.onlineUsers.users.splice(i, 1);  
                            //触发界面的更新  
                            self.onLeaveRoom(nickName, avatar);  
                        }  
                    }  
                }  
            });  
        },  
        onSuccess : function () {  
            console.log("监听成功")  
        },  
        onFailed : function () {  
            console.log("监听失败")  
        }  
    });  
};

2. 发送消息

  • 初始化一个chatMessage对象,包含发送方id,昵称,消息内容,消息类型为chat
  • 将chatMessage转换为一个Json格式的字符串
  • 调用GoEasy的Publish方法,完成消息的发送

参考代码(service.js)

this.sendMessage = function(content) {  
    var message = new ChatMessage(this.currentUser.id,this.currentUser.nickname, MessageType.CHAT, content);  
    var self = this;  
    this.goeasy.publish({  
        channel: self.currentRoomId,  
        message: JSON.stringify(message),  
        onSuccess: function() {  
            console.log("消息发布成功。");  
        },  
        onFailed: function(error) {  
            console.log("消息发送失败,错误编码:" + error.code + " 错误信息:" + error.content);  
        }  
    });  
};

3. 接收和显示新消息/道具

之前我们已经在初始化页面的时候执行了service.subscriberNewMessage(),当我们收到一条消息时:

  • 根据消息类型判断是一条聊天消息,还是一个道具
  • 如果收到的是一条聊天消息,直接显示到界面
  • 如果是道具,就播放动画

参考代码(service.js)

//监听消息或道具  
this.subscriberNewMessage = function() {  
    var self = this;  
    this.goeasy.subscribe({  
        channel: this.currentRoomId, //替换为您自己的channel  
        onMessage: function(message) {  
            var chatMessage = JSON.parse(message.content);  
            //todo:事实上不推荐在前端收到时保存, 一个用户开多个窗口,会导致重复保存, 建议所有消息都是都在发送时在服务器端保存,这里只是为了演示  
            self.restapi.saveChatMessage(self.currentRoomId, chatMessage);  
            //如果收到的是一个消息,就显示为消息  
            if (chatMessage.type == MessageType.CHAT) {  
                var selfSent = chatMessage.senderUserId == self.currentUser.id;  
                var content = JSON.parse(message.content);  
                self.onNewMessage(chatMessage.senderNickname, content, selfSent);  
            }  
            //如果收到的是一个道具,就播放道具动画  
            if (chatMessage.type == MessageType.PROP) {  
                if (chatMessage.content == Prop.ROCKET) {  
                    self.onNewRocket(chatMessage.senderNickname);  
                }  
                if (chatMessage.content == Prop.HEART) {  
                    self.onNewHeart(chatMessage.senderNickname);  
                }  
            }  
        }  
    });  
};

4. 发送和接收并展示道具

其实和发送消息的实现几乎是一样的,具体代码请参考service.js的sendProp方法,controller.js的onNewHeart()方法。动画的播放,使用了TweenMax这个库,主要是为了展示一个实现思路,小编也不知道这个库是否有很好的兼容性,以及是否能够用在Uniapp和小程序下,知道的朋友可以留言分享给大家。

this.sendProp = function(prop) {  
    var self = this;  
    var message = new ChatMessage(this.currentUser.id,this.currentUser.nickname, MessageType.PROP, prop);  
    this.goeasy.publish({  
        channel: self.currentRoomId,  
        message: JSON.stringify(message),  
        onSuccess: function() {  
            console.log("道具发布成功。");  
        },  
        onFailed: function(error) {  
            console.log("道具发送失败,错误编码:" + error.code + " 错误信息:" + error.content);  
        }  
    });  
};

至此,一个聊天室就搞定了,是不是很简单?

如果阅读本文或开发中有任何问题,也欢迎在GoEasy官网(https://www.goeasy.io)添加GoEasy为好友,来获得更多技术支持。

GoEasy系列教程:

收起阅读 »

uni-app自定义长按事件


<view   @touchmove="handletouchmove" @touchstart="handletouchstart" @touchend="handletouchend" >  
</view>  

             handletouchstart(e) {  
                this.timeOutEvent = setTimeout(() => {  
                    this.onLongPress(e)  
                }, 1000); //这里设置定时器,定义长按1000毫秒触发长按事件,时间可以自己改,  
                return false;  
            },  
            handletouchend() {  
                clearTimeout(this.time); //清除定时器    
                if (this.time != 0) {  
                    //处理点击时间  
                }  
                return false;  
            },  
            handletouchmove() {  
                clearTimeout(this.time); //清除定时器    
                this.time = 0;  
            },  
           onLongPress(e) {  
                    // 处理长按事件  
           }  
继续阅读 »

<view   @touchmove="handletouchmove" @touchstart="handletouchstart" @touchend="handletouchend" >  
</view>  

             handletouchstart(e) {  
                this.timeOutEvent = setTimeout(() => {  
                    this.onLongPress(e)  
                }, 1000); //这里设置定时器,定义长按1000毫秒触发长按事件,时间可以自己改,  
                return false;  
            },  
            handletouchend() {  
                clearTimeout(this.time); //清除定时器    
                if (this.time != 0) {  
                    //处理点击时间  
                }  
                return false;  
            },  
            handletouchmove() {  
                clearTimeout(this.time); //清除定时器    
                this.time = 0;  
            },  
           onLongPress(e) {  
                    // 处理长按事件  
           }  
收起阅读 »

uniAPP开发、团队开发uniapp

uniapp模板 5+App开发 移动APP uniapp

需求明确
沟通、整理和明确客户需求,撰写文档,搭建功能脑图架构
交互体验
用户体验设计、用户场景模拟、原型设计
UI设计
界面、色彩视觉设计、图标设计及布局设计
功能开发
服务器端、苹果及安卓端、前端H5开发/接口开发
测试验收
BUG修改、功能调整和优化、验收文档完善,上线应用市场
售后服务
后续技术维护、持续跟进、项目运营支撑
一⑤8叁2一①伍0玖九

继续阅读 »

需求明确
沟通、整理和明确客户需求,撰写文档,搭建功能脑图架构
交互体验
用户体验设计、用户场景模拟、原型设计
UI设计
界面、色彩视觉设计、图标设计及布局设计
功能开发
服务器端、苹果及安卓端、前端H5开发/接口开发
测试验收
BUG修改、功能调整和优化、验收文档完善,上线应用市场
售后服务
后续技术维护、持续跟进、项目运营支撑
一⑤8叁2一①伍0玖九

收起阅读 »