小程序开发 IM 聊天页面问题及其解决方案合集

3,210 阅读3分钟

首先,我们先写一下大概的结构和样式,我使用的是taro框架进下小程序的开发,原生或者其他框架也可以参考,没有太大的出入。

  .js
  
  componentWillMount(){
    let list = []
    for (let index = 0; index < 10; index++) {
      list.push(index)
    }
    this.setState({list})
  }
  handleMessageSubmit(){
    
  }
	render() {
		let { messageContent, list } = this.state;
		return (
			<View className='im-page'>
				<ScrollView scrollY className='im-page__list'>
          {
            list.map(item=>{
              return <View key={item} className='im-page__item'>
                内容:{item}
                <Image src='https://pic3.zhimg.com/80/v2-d18fc238468e504504dc6a7ee5f75a12_hd.jpg'></Image>
              </View>
            })
          }
        </ScrollView>
				<View className='im-page__tool'>
					<Input
						placeholder='对 Ta 发送消息'
						placeholderStyle={{ color: '#cccccc' }}
						value={messageContent}
						confirmType='send'
						onConfirm={this.handleMessageSubmit.bind(this)}
					/>
					<AtIcon prefixClass='icon' value='x_line' color='#333333' size='25' />
				</View>
			</View>
		);
	}
  
  
  .less
  
  .im-page__tool {
  position: fixed;
  bottom: 0;
  left: 0;
  width: 100%;
  background: #ffffff;
  border-top: 1px solid #ececec;
  display: flex;
  align-items: center;

  input {
    flex: 1;
    margin: 14px 0px 14px 24px;
    padding: 15px 24px;
    background: #f8f8f8;
    border-radius: 36px;
    font-size: 28px;
  }
  at-icon{
    padding: 24px;
    transform: rotate(45deg);
  }
}

接下来考虑底部输入栏,我们想要的效果是输入框聚焦时,底部的输入框上推至键盘上方,微信提供了adjustPosition这个属性,但是开启之后整个页面都会上推,体验非常不好。所以我们采取另一个方法onKeyboardHeightChange,获取到键盘的高度,动态的计算工具栏距离底部的距离。

修改后的代码片段

handleBlur() {
  this.setState({
      inputBottom: 0
  });
}
setInputBottom(e) {
  let keyHeight = e.detail.height;
  this.setState(
    (prev) => {
      if (keyHeight !== prev.inputBottom) {
        return {
          inputBottom: keyHeight + 'px',
        };
      }
    }
  );
}


<View className='im-page__tool' style={{ bottom: inputBottom}}>
  <Input
    placeholder='对 Ta 发送消息'
    placeholderStyle={{ color: '#cccccc' }}
    value={messageContent}
    confirmHold
    adjustPosition={false}
    confirmType='send'
    onKeyboardHeightChange={this.setInputBottom.bind(this)}
    onFocus={this.setInputBottom.bind(this)}
    onConfirm={this.handleMessageSubmit.bind(this)}
    onBlur={this.handleBlur.bind(this)}
  />
  <AtIcon prefixClass='icon' value='x_line' color='#333333' size='25' />
</View>

需要注意一点,部分安卓机型在第一次onKeyboardHeightChange触发的时候获取到的高度会不正确,所以可以在onFocus时也添加一个设置inputBottom的计算。

接下来是模拟微信聊天时页面自动滚动到最新聊天位置的效果,这个很简答,可以使用scrollView的 scrollToView 或 scrollTop,在数据加载完成和输入框聚焦时设置滚动到底部就可以了。 但是这里我们还是直接使用设置scrollTop的方法

代码片段如下

	constructor() {
		super(...arguments);
		this.state = {
      messageContent: '',
      list: [],
      inputBottom: 0,
      listScrollTop: 10000
    };
  }
  handleMessageSubmit(e){
    this.setState(prev=>{
      let list = prev.list
      list.push(e.detail.value)
      return {
        list,
        messageContent: '',
        listScrollTop: list.length * 10000,
      }
    })
  }
  
	render() {
		let { messageContent, list, inputBottom, listScrollTop } = this.state;
		return (
			<View className='im-page'>
				<ScrollView scrollY scrollTop={listScrollTop} className='im-page__list'>
          {
            list.map(item=>{
              return <View key={item} className='im-page__item'>
                内容:{item}
                <Image src='https://pic3.zhimg.com/80/v2-d18fc238468e504504dc6a7ee5f75a12_hd.jpg'></Image>
              </View>
            })
          }
        </ScrollView>
				<View className='im-page__tool' style={{ bottom: inputBottom}}>
          <Input
						placeholder='对 Ta 发送消息'
						placeholderStyle={{ color: '#cccccc' }}
						value={messageContent}
						confirmHold
						adjustPosition={false}
						confirmType='send'
						onKeyboardHeightChange={this.setInputBottom.bind(this)}
						onFocus={this.setInputBottom.bind(this)}
						onConfirm={this.handleMessageSubmit.bind(this)}
						onBlur={this.handleBlur.bind(this)}
          />
					<AtIcon prefixClass='icon' value='x_line' color='#333333' size='25' />
				</View>
			</View>
		);
	}
  

最后一个比较麻烦的点就是下拉加载更多了,要实现这个功能刚开始思路比较明确,就是在加载完成后滚动到目标位置,微信也提供了相关的api,但是在真机上就是卡顿,闪屏,非常不流畅,后来参考社区内一个回答的思路,终于可以比较流畅的实现这个功能了。

思路: 1.获取下一页数据后,将数据赋值到一个新的变量newList中。 2.在滚动区域外遍历newList,保证每个item的高度和scrollView中的item高度是一样的,设置newList列表为绝对定位,隐藏显示 3.获取newList的列表高度newListHeight。 4.在列表滚动结束后将newList 合并到 list 上,并让列表滚动至 newListHeight 的位置。

其中scrollView的滚动结束可以利用定时器来模拟实现 代码如下:

	onListScroll() {
		if (this.timeFun) {
			clearTimeout(this.timeFun);
			this.timeFun = null;
		}
		let vm = this;
		this.timeFun = setTimeout(function() {
			console.log(‘滚动结束)
		}, 300);
	}

完整代码 GitHub - xuxiaoxiao312/taroIM: 一个基于taro开发的聊天小程序demo