一、项目简述

taro-chatroom是基于Taro多端实例聊天项目,运用Taro+react+react-redux+taroPop+react-native等技术开发的仿微信App界面聊天室,实现了发送消息/emoj表情、gif表情大图、图片预览、发红包、动态圈等功能。

二、预览效果

编译到H5端、小程序、App端效果如下:(后续大图均为APP端)

三、技术栈

  • 编码/技术:Vscode + react/taro/redux/RN
  • iconfont图标:阿里字体图标库
  • 自定义导航栏Navigation + 底部Tabbar
  • 弹窗组件:taroPop(基于Taro封装自定义模态框)
  • 支持编译:H5端 + 小程序 + App端

/**
* @desc Taro入口页面 app.jsx
* @about Q:282310962 wx:xy190310
*/ import Taro, { Component } from '@tarojs/taro'
import Index from './pages/index' // 引入状态管理redux
import { Provider } from '@tarojs/redux'
import { store } from './store' // 引入样式
import './app.scss'
import './styles/fonts/iconfont.css'
import './styles/reset.scss' class App extends Component {
config = {
pages: [
'pages/auth/login/index',
'pages/auth/register/index',
'pages/index/index',
...
],
window: {
backgroundTextStyle: 'light',
navigationBarBackgroundColor: '#fff',
navigationBarTitleText: 'TaroChat',
navigationBarTextStyle: 'black',
navigationStyle: 'custom'
}
} // 在 App 类中的 render() 函数没有实际作用
// 请勿修改此函数
render () {
return (
<Provider store={store}>
<Index />
</Provider>
)
}
} Taro.render(<App />, document.getElementById('app'))

◆ Taro自定义顶部Navigation导航条 + Tabbar菜单

◆ 登录/注册验证模块

return (
<View className="taro__container flexDC bg-eef1f5">
<Navigation background='#eef1f5' fixed /> <ScrollView className="taro__scrollview flex1" scrollY>
<View className="auth-lgreg">
{/* logo */}
<View className="auth-lgreg__slogan">
<View className="auth-lgreg__slogan-logo">
<Image className="auth-lgreg__slogan-logo__img" src={require('../../../assets/taro.png')} mode="aspectFit" />
</View>
<Text className="auth-lgreg__slogan-text">欢迎来到Taro-Chatroom</Text>
</View>
{/* 表单 */}
<View className="auth-lgreg__forms">
<View className="auth-lgreg__forms-wrap">
<View className="auth-lgreg__forms-item">
<Input className="auth-lgreg__forms-iptxt flex1" placeholder="请输入手机号/昵称" onInput={this.handleInput.bind(this, 'tel')} />
</View>
<View className="auth-lgreg__forms-item">
<Input className="auth-lgreg__forms-iptxt flex1" placeholder="请输入密码" password onInput={this.handleInput.bind(this, 'pwd')} />
</View>
</View>
<View className="auth-lgreg__forms-action">
<TouchView onClick={this.handleSubmit}><Text className="auth-lgreg__forms-action__btn">登录</Text></TouchView>
</View>
<View className="auth-lgreg__forms-link">
<Text className="auth-lgreg__forms-link__nav">忘记密码</Text>
<Text className="auth-lgreg__forms-link__nav" onClick={this.GoToRegister}>注册账号</Text>
</View>
</View>
</View>
</ScrollView> <TaroPop ref="taroPop" />
</View>
)
/**
* @tpl 登录模块
*/ import Taro from '@tarojs/taro'
import { View, Text, ScrollView, Image, Input, Button } from '@tarojs/components' import './index.scss' import { connect } from '@tarojs/redux'
import * as actions from '../../../store/action'... class Login extends Taro.Component {
config = {
navigationBarTitleText: '登录'
}
constructor(props) {
super(props)
this.state = {
tel: '',
pwd: '',
}
}
componentWillMount() {
// 判断是否登录
storage.get('hasLogin').then(res => {
if(res && res.hasLogin) {
Taro.navigateTo({url: '/pages/index/index'})
}
})
}
// 提交表单
handleSubmit = () => {
let taroPop = this.refs.taroPop
let { tel, pwd } = this.state if(!tel) {
taroPop.show({content: '手机号不能为空', time: 2})
}else if(!util.checkTel(tel)) {
taroPop.show({content: '手机号格式有误', time: 2})
}else if(!pwd) {
taroPop.show({content: '密码不能为空', time: 2})
}else {
// ...接口数据
... storage.set('hasLogin', { hasLogin: true })
storage.set('user', { username: tel })
storage.set('token', { token: util.setToken() }) taroPop.show({
skin: 'toast',
content: '登录成功',
icon: 'success',
time: 2
}) ...
}
} render () {
...
}
} const mapStateToProps = (state) => {
return {...state.auth}
} export default connect(mapStateToProps, {
...actions
})(Login)

taro本地存储使用的是异步存储,由于同步存储不支持RN端

/**
* @desc Taro本地存储
*/ import Taro from '@tarojs/taro' export default class Storage {
static get(key) {
return Taro.getStorage({ key }).then(res => res.data).catch(() => '')
} static set(key, data){
return Taro.setStorage({key: key, data: data}).then(res => res)
} static del(key){
Taro.removeStorage({key: key}).then(res => res)
} static clear(){
Taro.clearStorage()
}
}

如不希望编译到RN端,使用如下包裹即可

/*postcss-pxtransform rn eject enable*/

/*postcss-pxtransform rn eject disable*/

对于一些RN端不兼容样式,边框、超过多行...,需特殊样式处理

/*
* 对于不兼容的样式,如RN不兼容border-right,可以通过mixin统一处理
*/ /**
* RN 不支持针对某一边设置 style,即 border-bottom-style 会报错
* 那么 border-bottom: 1px 就需要写成如下形式: border: 0 style color; border-bottom-width: 1px;
*/
@mixin border($dir, $width, $style, $color) {
border: 0 $style $color;
@each $d in $dir {
#{border-#{$d}-width}: $width;
}
} /**
* NOTE RN 无法通过 text-overflow 实现省略号,这些代码不会编译到 RN 中
*/
@mixin ellipsis {
/*postcss-pxtransform rn eject enable*/
overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
/*postcss-pxtransform rn eject disable*/
} /**
* NOTE 实现多行文本省略,RN 用 Text 标签的 numberOfLines={2},H5/小程序用 -webkit-line-clamp
*/
@mixin clamp($line) {
/*postcss-pxtransform rn eject enable*/
display: -webkit-box;
overflow: hidden;
-webkit-line-clamp:$line;
/* autoprefixer: ignore next */
-webkit-box-orient: vertical;
/*postcss-pxtransform rn eject disable*/
} /**
* 对于不能打包到 RN 的样式,可以用 postcss 方式引入
*/
@mixin eject($attr, $value) {
/*postcss-pxtransform rn eject enable*/
#{$attr}: $value;
/*postcss-pxtransform rn eject disable*/
}

◆ Taro聊天实现消息滚动到底部

在taro中实现聊天消息滚动到最底部,由于RN端不支持 createSelectorQuery,如是做了兼容处理。

componentDidMount() {
if(process.env.TARO_ENV === 'rn') {
this.scrollMsgBottomRN()
}else {
this.scrollMsgBottom()
}
}
// 滚动至聊天底部
scrollMsgBottom = () => {
let query = Taro.createSelectorQuery()
query.select('#scrollview').boundingClientRect()
query.select('#msglistview').boundingClientRect()
query.exec((res) => {
// console.log(res)
if(res[1].height > res[0].height) {
this.setState({ scrollTop: res[1].height - res[0].height })
}
})
}
scrollMsgBottomRN = (t) => {
let that = this
this._timer = setTimeout(() => {
that.refs.ScrollViewRN.scrollToEnd({animated: false})
}, t ? 16 : 0)
}

聊天中表情使用的是emoj表情符,如果使用图片做表情也是可以,不过需要一些特殊匹配处理 :12)  [:高兴],使用emoj就相对简单些罢了。

// 渲染消息记录
renderMsgTpl = (data) => {
return data.map((item, index) => (
<View key={index}>
{item.msgtype == 1 &&
<View className="msgitem msg__time"><Text className="msg__text">{item.msg}</Text></View>
} {item.msgtype == 2 &&
<View className="msgitem msg__notice"><Text className="msg__text">{item.msg}</Text></View>
} {item.msgtype == 3 &&
<View className="msgitem">
{!item.isme ? <View className="msg__avator"><Image className="msg__avator-img" src={item.avator} mode="aspectFit" /></View> : null}
<View className={`msg__cntbox ${item.isme ? 'msg-me' : 'msg-others'}`}>
<Text className="msg-author">{item.author}</Text>
<View className={`msg__cnt ${item.isme ? 'msg__cnt-me' : 'msg__cnt-others'}`} onLongPress={this.handleLongPressMenu}>
<Text className="msg__cnt-text">{item.msg}</Text>
</View>
</View>
{item.isme ? <View className="msg__avator"><Image className="msg__avator-img" src={item.avator} mode="aspectFit" /></View> : null}
</View>
} {item.msgtype == 4 &&
<View className="msgitem">
{!item.isme ? <View className="msg__avator"><Image className="msg__avator-img" src={item.avator} mode="aspectFit" /></View> : null}
<View className={`msg__cntbox ${item.isme ? 'msg-me' : 'msg-others'}`}>
<Text className="msg-author">{item.author}</Text>
<View className={`msg__cnt ${item.isme ? 'msg__cnt-me' : 'msg__cnt-others'} msg__lgface`} onLongPress={this.handleLongPressMenu}>
<Image className="msg__lgface-img" src={item.imgsrc} mode="widthFix" />
</View>
</View>
{item.isme ? <View className="msg__avator"><Image className="msg__avator-img" src={item.avator} mode="aspectFit" /></View> : null}
</View>
} {item.msgtype == 5 &&
<View className="msgitem">
{!item.isme ? <View className="msg__avator"><Image className="msg__avator-img" src={item.avator} mode="aspectFit" /></View> : null}
<View className={`msg__cntbox ${item.isme ? 'msg-me' : 'msg-others'}`}>
<Text className="msg-author">{item.author}</Text>
<View className={`msg__cnt ${item.isme ? 'msg__cnt-me' : 'msg__cnt-others'} msg__picture`} onClick={this.handlePreviewPicture.bind(this, item.imgsrc)} onLongPress={this.handleLongPressMenu}>
<Image className="msg__picture-img" src={item.imgsrc} mode="widthFix" />
</View>
</View>
{item.isme ? <View className="msg__avator"><Image className="msg__avator-img" src={item.avator} mode="aspectFit" /></View> : null}
</View>
} ...
</View>
))
}

以上就是taro开发聊天室的一些分享,今天就介绍到这里,希望能有些许的帮助~~

Taro聊天室|react+taro仿微信聊天App界面|taro聊天实例的更多相关文章

  1. react聊天室|react+redux仿微信聊天IM实例|react仿微信界面

    一.项目概况 基于react+react-dom+react-router-dom+redux+react-redux+webpack2.0+react-photoswipe+swiper等技术混合开 ...

  2. uni-app聊天室|vue+uniapp仿微信聊天实例|uniapp仿微信App界面

    一.介绍 运用UniApp+Vue+Vuex+swiper+uniPop等技术开发的仿微信原生App聊天室|仿微信聊天界面实例项目uniapp-chatroom,实现了发送图文消息.表情(gif图), ...

  3. Vue3.0聊天室|vue3+vant3仿微信聊天实例|vue3.x仿微信app界面

    一.项目简介 基于Vue3.0+Vant3.x+Vuex4.x+Vue-router4+V3Popup等技术开发实现的仿微信手机App聊天实例项目Vue3-Chatroom.实现了发送图文表情消息/g ...

  4. vue聊天室|h5+vue仿微信聊天界面|vue仿微信

    一.项目简介 基于Vue2.0+Vuex+vue-router+webpack2.0+es6+vuePhotoPreview+wcPop等技术架构开发的仿微信界面聊天室——vueChatRoom,实现 ...

  5. electron聊天室|vue+electron-vue仿微信客户端|electron桌面聊天

    一.项目概况 基于Electron+vue+electron-vue+vuex+Nodejs+vueVideoPlayer+electron-builder等技术仿制微信电脑端界面聊天室实例,实现消息 ...

  6. Svelte3聊天室|svelte+svelteKit仿微信聊天实例|svelte.js开发App

    基于svelte3.x+svelteKit构建仿微信App聊天应用svelte-chatroom. svelte-chatroom 基于svelte.js+svelteKit+mescroll.js+ ...

  7. Tauri-Vue3桌面端聊天室|tauri+vite3仿微信|tauri聊天程序EXE

    基于tauri+vue3.js+vite3跨桌面端仿微信聊天实例TauriVue3Chat. tauri-chat 运用最新tauri+vue3+vite3+element-plus+v3layer等 ...

  8. Next.js+React聊天室|Next仿微信桌面端|next.js聊天实例

    一.项目介绍 next-webchat 基于Next.js+React.js+Redux+Antd+RScroll+RLayer等技术构建的PC桌面端仿微信聊天项目.实现了消息/表情发送.图片/视频预 ...

  9. workerman-chat(PHP开发的基于Websocket协议的聊天室框架)(thinkphp也是支持socket聊天的)

    workerman-chat(PHP开发的基于Websocket协议的聊天室框架)(thinkphp也是支持socket聊天的) 一.总结 1.下面链接里面还有一个来聊的php聊天室源码可以学习 2. ...

随机推荐

  1. Java描述设计模式(11):观察者模式

    本文源码:GitHub·点这里 || GitEE·点这里 一.观察者模式 1.概念描述 观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式.观察者模式定义了一种一对多 ...

  2. Python 接口自动化常用方法封装

    #!/usr/bin/env python # -*- coding:utf-8 -*- # ************************************* # @Time : 2019/ ...

  3. Nginx配置Tomcat8反向代理出现 java.lang.IllegalArgumentException: The character [_] is never valid in a domain name.

    在配置Nginx的反向代理,访问反向代理的路径而出现了异常 java.lang.IllegalArgumentException: The character [_] is never valid i ...

  4. C# 波浪线绘制

    波浪线效果如上 界面绘制操作 private Point? _startPoint = null; private void ContainerCanvas_OnPreviewMouseLeftBut ...

  5. Selenium(一):原理与安装、简单的使用

    1. selenium原理 1.1 selenium介绍 Selenium是一个Web应用的自动化框架. 通过它,我们可以写出自动化程序,像人一样在浏览器里操作web界面. 比如点击界面按钮,在文本框 ...

  6. 关于mybtis 使用过程中发生There is no getter for property named 'id' in class 'java.lang.String' 错误

    今天在修改一个关于mybtis语句时,偶然发现的一个错误  There is no getter for property named 'id' in class 'java.lang.String' ...

  7. [转]VB.net中 excel 的range方法

    本文转自:https://blog.csdn.net/bigheadsheep/article/details/7711235 第五章 Range对象基本操作应用示例(1)Range对象可能是VBA代 ...

  8. UI视图控件、视图嵌套、SubView、Tag 的使用

    - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchO ...

  9. SQL Server有意思的数据类型隐式转换问题

    写这篇文章的时候,还真不知道如何取名,也不知道这个该如何将其归类.这个是同事遇到的一个案例,案例比较复杂,这里抽丝剥茧,仅仅构造一个简单的案例来展现一下这个问题.我们先构造测试数据,如下所示: CRE ...

  10. sakura设置桌面壁纸

    下下载steam上的Wallpaper Engine 先将sakura.html下载为html文件. 再从文件打开 就保存了 再加载保存,就一直是了