Taro聊天室|react+taro仿微信聊天App界面|taro聊天实例
一、项目简述
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菜单
- 由于项目需求,顶部导航栏/底部tabbar均为自定义组件,详情见:Taro多端自定义导航栏Navbar+Tabbar实例
- 项目中用到的弹窗插件是基于Taro自定义模态框组件,参看:Taro自定义模态框组件
◆ 登录/注册验证模块
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聊天实例的更多相关文章
- react聊天室|react+redux仿微信聊天IM实例|react仿微信界面
一.项目概况 基于react+react-dom+react-router-dom+redux+react-redux+webpack2.0+react-photoswipe+swiper等技术混合开 ...
- uni-app聊天室|vue+uniapp仿微信聊天实例|uniapp仿微信App界面
一.介绍 运用UniApp+Vue+Vuex+swiper+uniPop等技术开发的仿微信原生App聊天室|仿微信聊天界面实例项目uniapp-chatroom,实现了发送图文消息.表情(gif图), ...
- Vue3.0聊天室|vue3+vant3仿微信聊天实例|vue3.x仿微信app界面
一.项目简介 基于Vue3.0+Vant3.x+Vuex4.x+Vue-router4+V3Popup等技术开发实现的仿微信手机App聊天实例项目Vue3-Chatroom.实现了发送图文表情消息/g ...
- vue聊天室|h5+vue仿微信聊天界面|vue仿微信
一.项目简介 基于Vue2.0+Vuex+vue-router+webpack2.0+es6+vuePhotoPreview+wcPop等技术架构开发的仿微信界面聊天室——vueChatRoom,实现 ...
- electron聊天室|vue+electron-vue仿微信客户端|electron桌面聊天
一.项目概况 基于Electron+vue+electron-vue+vuex+Nodejs+vueVideoPlayer+electron-builder等技术仿制微信电脑端界面聊天室实例,实现消息 ...
- Svelte3聊天室|svelte+svelteKit仿微信聊天实例|svelte.js开发App
基于svelte3.x+svelteKit构建仿微信App聊天应用svelte-chatroom. svelte-chatroom 基于svelte.js+svelteKit+mescroll.js+ ...
- Tauri-Vue3桌面端聊天室|tauri+vite3仿微信|tauri聊天程序EXE
基于tauri+vue3.js+vite3跨桌面端仿微信聊天实例TauriVue3Chat. tauri-chat 运用最新tauri+vue3+vite3+element-plus+v3layer等 ...
- Next.js+React聊天室|Next仿微信桌面端|next.js聊天实例
一.项目介绍 next-webchat 基于Next.js+React.js+Redux+Antd+RScroll+RLayer等技术构建的PC桌面端仿微信聊天项目.实现了消息/表情发送.图片/视频预 ...
- workerman-chat(PHP开发的基于Websocket协议的聊天室框架)(thinkphp也是支持socket聊天的)
workerman-chat(PHP开发的基于Websocket协议的聊天室框架)(thinkphp也是支持socket聊天的) 一.总结 1.下面链接里面还有一个来聊的php聊天室源码可以学习 2. ...
随机推荐
- Chrome远程调试手机端UC浏览器
今天在手机UC上发现我的一个网页打不开,而在PC上是正常的,因此需要通过Chrome远程调试手机端UC浏览器查下问题,折腾了老久才弄好. 获取 Google USB 驱动程序 首先将手机通过USB接口 ...
- 什么是StatefulSet
简单说来,StatefulSet其实就是一种升级版的Deployment,大体工作原理如下 1.为每个Pod名字按顺序编号,按顺序启动 # kubectl get po -o wide -l app= ...
- .Netcore Swagger - 解决外部库导致的“Actions require an explicit HttpMethod binding for Swagger 2.0”
现象: 项目中导入Ocelot后,swagger页面无法正常显示,查看异常发现 Ocelot.Raft.RaftController 中的 Action 配置不完全,swagger扫描时不能正确生成 ...
- 文本分类Pipeline
- Go--实现两个大数相乘
----- import ( "bufio" "fmt" "os" "strings" ) func multi(str ...
- LeetCode刷题191119
博主渣渣一枚,刷刷leetcode给自己瞅瞅,大神们由更好方法还望不吝赐教.题目及解法来自于力扣(LeetCode),传送门. 算法: 给定一个非负整数数组,你最初位于数组的第一个位置. 数组中的每个 ...
- Python语法速查: 5. 运算符、math模块、表达式
返回目录 (1)一些较容易搞错的运算符 一般简单的如加减乘除之类的运算符就不写了,这里主要列些一些容易搞错或忘记的运算符.运算符不仅仅只有号,有一些英文单词如 in, and 之类,也是运算符,并不是 ...
- acwing 517. 信息传递
地址 https://www.acwing.com/problem/content/description/519/ 有 n 个同学(编号为 1 到 n)正在玩一个信息传递的游戏. 在游戏里每人都有一 ...
- echarts常用说明
import { Injectable } from '@angular/core'; //模板option通用 let fff7 = '#fff'; //字体统一颜色rgba(255,255,255 ...
- 【Notepad++】notepad++主题和字体设置(非常好看舒服的)
#效果图 1.字体:Courier New 字号:14号字体 2.字体:Consolas 字号:14号字体 #设置方法 1.设置---语言格式设置 2.选择主题,同时勾选“使用全局字体”“使用全局字体 ...