项目需求:用uniapp开发微信小程序,直播界面做可拖动弹幕。

一、直播页面,微信小程序的<live-player>组件就是用来搭建直播的。
我的项目需求是从上一个页面点击后直接跳转进对应的直播间,所以这个界面一开始就需要是全屏播放的。
1.<live-player>的全屏.

<live-player
	id="liveplayer"
	src="https://domain/pull_stream"
	mode="live"
	:autoplay="true"
	orientation="vertical"
	object-fit="fillCrop"
	@statechange="statechange"
	@error="error"
>
</live-player>

其中一个必不可少的属性就是id,这个id之后会用来在JS代码中获取live-player的上下文。

// 创建直播流对象,获取live-player的上下文。
let player = uni.createLivePlayerContext('liveplayer', this);

拿到这个上下文之后就可以通过调用方法实现全屏和退出全屏

// 直播流对象全屏
player.requestFullScreen();
//退出全屏
player.exitFullScreen();
2.直播弹幕区
有了全屏的直播后,需要在这个直播上放一个弹幕区域和按钮等。由于  map、video、canvas、camera、live-player、live-pusher都是原生组件,层级比前端组件高,即便使用了z-index前端组件也是无法覆盖到原生组件上的。所以采用官方提供的`<cover-view>`进行覆盖,这里注意,`<cover-view/> 内只能嵌套 <cover-view/> <cover-image/> <button/> <navigator/> <ad/>,其他标签的子节点树在真机上都会被忽略。`

有了这些限制,无法用<scroll-view>做一个可上下拖动的弹幕。所以采用以下的方法。

官方文档中,给cover-view提供了一个css的属性overflow-y: scroll;(官网上写的是overflow: scroll;试过了没有用),配合:scroll-top=“scrollTop+‘px’” 或 :scroll-top=" '"这两段代码让设定了高度的区域在内容溢出后实现可拖动,我这里使用了前者,因为后面在发送弹幕后还要实现自动定位到最后一行。

<!-- 弹幕 -->
<cover-view class="barrage" id="message-items" :scroll-top="scrollTop+'px'">
	<cover-view class="barrageItem" v-for="(item,index) in msgList" :key="index">
		<cover-view class="barrageIcon" :class="item.barrageIcon == '铁粉'? 'ironBarrageIcon' : 'newBarrageIcon'">{{item.barrageIcon}}</cover-view>
		<cover-view class="barrageName">{{item.barrageName}}:</cover-view>
		<cover-view class="barrageContent">{{item.barrageContent}}</cover-view>
	</cover-view>
</cover-view>
.barrage{
	width:380rpx;
	height:440rpx;
	margin: 0 0 20px 0;
	overflow-y: scroll;
	.barrageItem{
		line-height: 21px;
		margin: 5px 0;
		.barrageIcon{
			display: inline-block;
			color: rgba(255, 255, 255, 1);
			font-size: 14px;
			border-radius: 5px;
			text-align: center;
			line-height: 21px;
			width: 37px;
			height: 21px;
		}
		.newBarrageIcon{
			background-color: rgba(0, 186, 173, 1);
		}
		.ironBarrageIcon{
			background-color: rgba(255, 141, 26, 1);
		}
		.barrageName{
			display: inline-block;
			margin-left: 6px;
			color: rgba(255, 255, 255, 1);
			font-size: 12px;
		}
		.barrageContent{
			display: inline-block;
			color: rgba(229, 229, 229, 1);
			font-size: 12px;
		}
	}
}

到这里弹幕就搭建好了,现在需要在页面加载完成后 以及 发送完弹幕后,弹幕区域自动定位在最后一行。写JS代码。

// 弹幕滚动到底部,每次发送弹幕后需调用
letBarrageToButtom(){
	var _this = this;
	const query = uni.createSelectorQuery().in(this);
	query.select('.barrageItem').boundingClientRect(rect => {
		//这里的逻辑就是,把每条弹幕的高度x弹幕的条数,再加上每条弹幕之间的距离,margin值,这个10根据自己的样式调整
		var toTop = (_this.msgList.length*rect.height+(rect.height*10));
		// _this.setData({scrollTop:toTop});
		this.scrollTop = toTop;
	}).exec();
},

其中 uni.createSelectorQuery() 和 .select(’ ').boundingClientRect 都是官方提供的方法,地址随后贴上来。
3.input输入框的伪代码
现在有了可拖动的弹幕区域,就需要有个可以发送弹幕的input框,但是在<scroll-view>中不能嵌套input框,否则在真机上会被隐藏。好消息是,虽然input框被隐藏了,但是依旧可以通过方法获取焦点并获取到用户输入的内容。

HTML

<!-- 弹幕输入框 -->
<cover-view class="barrageInput" @tap='tapInput'>
	<cover-view class='text' :class="inputInfo == '写评论……'? 'placeholder' : ''">{{inputInfo}}</cover-view>
	<input class='input' v-model="inputModel" focus='inputFocus' @input='inputing' @blur='blurInput' type="text" confirm-type="send" @confirm="sendBarrage"></input>
</cover-view>

css

.barrageInput{
	width: 280px;
	height: 35px;
	line-height: 35px;
	background-color: rgba(0, 0, 0, 0.4);
	border-radius: 18px;
	.placeholder{
		color: rgba(229, 229, 229, 1);
		font-size: 13px;
		letter-spacing: 2px;
	}
	.text{
	  height: 35px;
	  line-height: 35px;
	  color: white;
	  margin: 0 15px;
	}
	.input{
	  height: 35px;
	  line-height: 35px;
	  color: white;
	  margin: 0 15px;
	  /* margin-top为text的高度,保持视觉上一致 */
	  margin-top: -35px; 
	}
}

JS

// 将焦点给到 input(在真机上不能获取input焦点)
tapInput() {
	//在真机上将焦点给input
	this.inputFocus = true,
	//初始占位清空
	this.inputInfo = ''
},
// input 输入时将 input 的输入内容给到cover-view
inputing(e){
	this.inputInfo = e.detail.value;
},
// input失焦时还原占位符
blurInput(e) {
	this.inputInfo = '写评论……';
	this.inputModel = '';
},
// 点击键盘右下角发送,发送弹幕
sendBarrage(){
	// 先判断要发送的信息是否为空,避免向后台发送无用信息
	if(this.inputInfo != ''){
		let barrage = {
			id:20,
			barrageIcon: '新粉',
			barrageName: '新的弹幕',
			barrageContent:this.inputInfo,
			barrageMoreContent:''
		}
		barrage = this.getNewline(barrage);
		this.msgList.push(barrage);
		this.inputModel = '';
		this.inputInfo = '写评论……';
		this.inputFocus = false;
		this.letBarrageToButtom();
	}else{
		return;
	}
}

到此,弹幕的发送功能也完成了。
4.弹幕的换行。
在做测试的时候发现,当弹幕字数较多时,并不会自动换行,而是超出部分直接隐藏了。这是因为<scroll-view>默认的样式有 white-space: nowrap; line-height: 1.2; display: block;
所以在需要换行的地方加了一行white-space:pre-wrap;虽然可以换行,但是content部分在块内换行,(弹幕内容区独自换行,前面的徽标和名字不动,样式有问题。)这里解决了好久,因为业务原因没有找到合适的方法,于是选择了手动裁切弹幕内容。

<!-- 弹幕 -->
<cover-view class="barrage" id="message-items" :scroll-top="scrollTop+'px'">
	<cover-view class="barrageItem" v-for="(item,index) in msgList" :key="index">
		<cover-view class="barrageIcon" :class="item.barrageIcon == '铁粉'? 'ironBarrageIcon' : 'newBarrageIcon'">{{item.barrageIcon}}</cover-view>
		<cover-view class="barrageName">{{item.barrageName}}:</cover-view>
		<cover-view class="barrageContent">{{item.barrageContent}}</cover-view>
		<cover-view class="barrageMoreContent" v-if="item.barrageMoreContent">{{item.barrageMoreContent}}</cover-view>
	</cover-view>
</cover-view>

css

.barrageMoreContent{
	color: rgba(229, 229, 229, 1);
	font-size: 12px;
	width: 100%;
	// 文字换行
	white-space:pre-wrap;
	//下面两行用于纯数字弹幕的换行,由于一串数字在计算机看来是一个长单词,如果不手动设置,是不会在单词内换行的,导致超出部分依旧被隐藏而不是换行。
	word-wrap: break-word;
	word-break: break-all;
}

JS

// 裁切数字换行
getNewline(list){
	let _listMsg = list.barrageContent;
	if(list.barrageName.length + list.barrageContent.length > 11){
		list.barrageContent = _listMsg.slice(0,11 - list.barrageName.length);
		list.barrageMoreContent = _listMsg.slice(11 - list.barrageName.length);
	}
	return list;
}
这段代码在初始化弹幕和发送弹幕后调用即可。个人感觉不是好的解决办法,以后有了更好的来更换代码。

最后效果图如下:
在这里插入图片描述

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐