功能:在小程序中调取手机相机录制视频,录制过程中提词器中字体跟着滚动,提词器颜色改变。
效果展示
提词器不显示:
修改提词器样式:
内容编写:
提词器下拉增加高,提词器可上下拖动:
开始录制(再次点击接受录制返回视频路径):
退出录制:
以下粘贴为全部代码,即拿即用:
<template>
<view class="box">
<view class="fixedTop">
<view
class="tui-status-bar"
:style="{ height: statusBarHeight + 'px' }"
></view>
<view class="btn" :style="{height:height+'px'}">
<image src="https://doctor.sdxxtop.com/img/tools/teleprompterClone.png" @click="showBackPopUp = !showBackPopUp"></image>
<image v-if="showTextPopUp" src="https://doctor.sdxxtop.com/img/tools/teleprompterShow.png" @click="showTextPopUp = false"></image>
<image v-else src="https://doctor.sdxxtop.com/img/tools/teleprompterUnShow.png" @click="showTextPopUp = true"></image>
</view>
<view class="textPopUp" v-if="showTextPopUp" :style="'margin-top:'+dragPopUpHeight+'px'" @touchmove="handleTouchMovePopUp" @touchstart="touchstartPopUp($event)">
<view class="text" :style="{ height: dragHeight + 'px',color:textColor }">
<scroll-view scroll-y :style="{ height: dragHeight + 'px' }" lower-threshold="0" :scroll-top="scrollTop" @scrolltolower="scrollBottom">
{{text?text:'你好,欢迎来到医生医影提词器,已为你开启智能跟随模式,台词跟着语速走,念到哪走到哪,节奏由你掌控;点击左下角设置按钮,可以修改台词样式配快来尝试录制示下吧~'}}
</scroll-view>
</view>
<view class="textBtn">
<image @click="teleprompterSet" src="https://doctor.sdxxtop.com/img/tools/teleprompterSet.png"></image>
<image @click="teleprompterLog" src="https://doctor.sdxxtop.com/img/tools/teleprompterLog.png"></image>
<image @touchmove.stop="handleTouchMove" @touchstart="touchstart($event)" src="https://doctor.sdxxtop.com/img/tools/teleprompterZoom.png"></image>
</view>
</view>
<view class="backPopUp" v-if="showBackPopUp">
<text></text>
<view @click="quit">
<image src="https://doctor.sdxxtop.com/img/tools/teleprompterBack.png"></image>
<text>退出拍摄页</text>
</view>
<view @click="againTake">
<image src="https://doctor.sdxxtop.com/img/tools/teleprompterAgain.png"></image>
<text>重新拍摄</text>
</view>
</view>
</view>
<view class="content">
<camera :device-position="devicePosition ? 'back' : 'front'" flash="off" @error="loadCameraError" :style="{width: '100%', height: winHeight+'px'}"></camera>
</view>
<!-- 开始录制 -->
<view class="start">
<image src="https://doctor.sdxxtop.com/img/tools/teleprompterStart.png" v-if="!isStartRecord" @click="startVideotape"></image>
<image src="https://doctor.sdxxtop.com/img/tools/teleprompterStop.png" v-else @click="stopVideoTape"></image>
<view class="turn" @click="turnCamera" v-if="!isStartRecord">
<image src="https://doctor.sdxxtop.com/img/tools/teleprompterTurn.png"></image>
<view>翻转</view>
</view>
<view class="time" v-if="isStartRecord">{{formatSeconds(recordTime)}}</view>
</view>
<!-- 蒙层 -->
<view class="mongolian" v-if="mongolian">
<view class="colorBox">
<view v-for="(item,index) in 9" :class="chooseColorIndex == index ? 'chooseColor' : '' " @click="chooseColor(index)"></view>
</view>
</view>
<!-- 修改编写 -->
<view class="editText" v-if="editTextPopUp" :style="{ marginTop : ( statusBarHeight + 44 ) +'px' }">
<view class="editTextTitle">
<text @click="cancelText">取消</text>
<text>修改编写</text>
<text @click="confirmText">确认</text>
</view>
<view class="editTextContent">
<textarea maxlength="-1" v-model="text" :style="'height:'+(winHeight-200)+'px;'" placeholder="请输入或粘贴"></textarea>
</view>
</view>
</view>
</template>
<script>
import { Debounce } from "@/utils/debounce.js";
export default {
data(){
return {
// 手机高度
winHeight: 0,
//状态栏高度
statusBarHeight: 0,
//header高度
height: 44,
// 显示提词器弹窗
showTextPopUp:true,
// 触摸开始的Y坐标
startY: 0,
// 拖动区域的高度
dragHeight: 150,
// 最大高度限制
maxHeight: 450 ,
// 整个弹窗触摸开始y的坐标
startPopUpY:0,
// 拖动区域的高度
dragPopUpHeight: 0,
// 选中颜色下标
chooseColorIndex:0,
// 动态字体颜色
textColor:"#FFFFFF",
// 是否显示蒙层
mongolian:false,
// 内容
text:"",
// 编辑文字弹窗
editTextPopUp:false,
// 相机内置
ctx:null,
// 录制生成的视频
createVideoUrl:'https://yishivideos.bj.bcebos.com/jinan_demo/video/20240328/d8a426d9a9ef37b2a28040fbcd470f28.mp4',
// 是否开始录制
isStartRecord:false,
// 前后置摄像头 true:前置 false:后置
devicePosition:true,
// 录制时长
recordTime:0,
// 录制时长定时器
recordTimeFun:null,
// 显示返回弹窗
showBackPopUp:false,
// 提词器移动定时事件
scrollTopTimeFun:null,
// 滚动条位置
scrollTop:0
}
},
created() {
uni.getSystemInfo({
success: (res) => {
this.statusBarHeight = res.statusBarHeight
}
})
},
onLoad(option) {
var that = this;
if(option.text){
this.text = option.text
}
uni.getSystemInfo({
success: function(res) {
that.winHeight = res.windowHeight
},
});
},
onReady() {
this.ctx = uni.createCameraContext();
},
methods:{
// 移动到底部
scrollBottom(e){
clearInterval(this.scrollTopTimeFun)
},
// 重新拍摄
againTake(){
this.text = '';
this.isStartRecord = false;
this.showBackPopUp = false;
this.scrollTopTimeFun = null;
},
// 退出
quit(){
uni.navigateBack()
},
// 处理时分秒
formatSeconds(seconds) {
seconds = Math.floor(seconds);
var hours = Math.floor(seconds / 3600);
var minutes = Math.floor((seconds - (hours * 3600)) / 60);
var secs = seconds % 60;
return [ minutes, secs ].map(num => num < 10 ? '0' + num : num).join(':');
},
// 取消文字
cancelText(){
this.editTextPopUp = false
this.text = ''
},
// 确认文字
confirmText(){
this.editTextPopUp = false
},
// 切换颜色
chooseColor(index){
this.chooseColorIndex = index
if(index == 0){
this.textColor = '#FFFFFF'
}else if(index == 1){
this.textColor = '#424242'
}else if(index == 2){
this.textColor = '#000000'
}else if(index == 3){
this.textColor = '#FA797D'
}else if(index == 4){
this.textColor = '#FB6F24'
}else if(index == 5){
this.textColor = '#F9D63C'
}else if(index == 6){
this.textColor = '#77E46F'
}else if(index == 7){
this.textColor = '#5A95EF'
}else if(index == 8){
this.textColor = '#998BFC'
}
},
// 提词器设置
teleprompterSet(){
this.mongolian = !this.mongolian
},
// 编辑文字
teleprompterLog(){
this.editTextPopUp = !this.editTextPopUp
},
// 移动整个弹窗起始点
touchstartPopUp(e){
this.startPopUpY = e.changedTouches[0].clientY
},
// 移动整个弹窗
handleTouchMovePopUp(event){
// 获取触摸点信息
const touch = event.touches[0];
if (this.startPopUpY === 0) {
this.startPopUpY = touch.clientY;
} else {
// 计算移动的距离
const diff = touch.clientY - this.startPopUpY;
// 更新拖动区域高度
if(this.dragPopUpHeight + diff >= 300){
this.dragPopUpHeight = 300
this.startPopUpY = 0
return
}
if( diff > 0 ){
this.dragPopUpHeight = Math.max(this.dragPopUpHeight, this.dragPopUpHeight + diff);
}else{
this.dragPopUpHeight = Math.max(0, this.dragPopUpHeight + diff);
}
// 重置起始Y坐标
this.startPopUpY = touch.clientY;
}
},
//开始触摸时
touchstart(e) {
this.startY = e.changedTouches[0].clientY
},
// 触摸移动
handleTouchMove(event){
// 获取触摸点信息
const touch = event.touches[0];
// 如果是第一次触摸
if (this.startY === 0) {
this.startY = touch.clientY;
} else {
// 计算移动的距离
const diff = touch.clientY - this.startY;
// 更新拖动区域高度
if(this.dragHeight + diff < 150){
this.dragHeight = 150
this.startY = 0
return
}
this.dragHeight = Math.min(this.maxHeight, this.dragHeight + diff);
// 重置起始Y坐标
this.startY = touch.clientY ;
}
},
// 防抖
Debounce: Debounce(function(e) {
var that = this;
if(e == 1){
that.scrollTopTimeFun = setInterval(()=>{
this.scrollTop++;
},80)
this.ctx.startRecord({
timeout:300,
success: (res) => {
that.isStartRecord = true
that.recordTimeFun = setInterval(()=>{
that.recordTime++
},1000)
console.log(res,"开始")
},
});
}else{
clearInterval(that.recordTimeFun)
clearInterval(that.scrollTopTimeFun)
uni.showLoading({
title:"上传中..."
})
that.isStartRecord = false
that.recordTime = 0
this.ctx.stopRecord({
success:(res=>{
uni.uploadFile({
url: 'https://xxx/admin/index/uploadVideo',
name: 'file',
header: {},
formData: {},
filePath: res.tempVideoPath,
success(res) {
var d = JSON.parse(res.data.replace(/\ufeff/g, '') || '{}');
that.createVideoUrl = d.data.url
uni.hideLoading()
uni.navigateTo({
url: '/pages/teleprompter/playVideo?url='+that.createVideoUrl
})
},
})
})
})
}
}, 1500),
// 开始录制
startVideotape(){
this.Debounce(1)
},
// 暂停录制
stopVideoTape(){
this.Debounce(2)
},
// 翻转相机
turnCamera(){
this.devicePosition = !this.devicePosition
},
// 加载相机错误
loadCameraError(e) {
console.log(e.detail);
}
}
}
</script>
<style lang="scss" scoped>
.box{
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
.fixedTop{
background-color: black;
position: fixed;
width: 100%;
top: 0;
left: 0;
z-index: 123;
.tui-status-bar {
width: 100%;
}
.btn{
padding-right: 250rpx;
display: flex;
align-items: center;
justify-content: space-between;
padding-left: 30rpx;
image{
width: 50rpx;
height: 50rpx;
}
}
.textPopUp{
position: absolute;
top:100%;
left: 50%;
z-index: 123;
width: 90%;
box-sizing: border-box;
background-color: #00000050;
padding: 30rpx;
border-radius: 20rpx;
transform: translateX(-50%);
.text{
font-size: 35rpx;
color: #FFFFFF;
line-height: 63rpx;
overflow: hidden;
}
.textBtn{
margin-top: 60rpx;
display: flex;
align-items: center;
justify-content: space-between;
image{
width: 43rpx;
height: 43rpx;
}
image:nth-of-type(1){
height: 36rpx
}
image:nth-of-type(2){
width: 35rpx;
height: 35rpx;
}
}
}
.backPopUp{
position: absolute;
top: 115%;
left: 20rpx;
z-index: 1111;
background-color: white;
border-radius: 20rpx;
font-size: 28rpx;
&>text{
border-color: #ff450000 #ff450000 white #ff450000;
display: inline-block;
width: 0;
height: 0;
border-width: 20rpx;
border-style: solid;
position: absolute;
top: 0%;
transform: translateY(-90%);
left: 15rpx;
}
&>view{
padding: 30rpx;
display: flex;
align-items: center;
&>image{
width: 32rpx;
height: 32rpx;
margin-right: 20rpx;
}
}
&>view:nth-of-type(1){
border-bottom: 2rpx solid #EFEFEF;
color: #621AA4;
&>image{
width: 40rpx;
height: 32rpx;
}
}
}
}
.start{
position: fixed;
bottom: 180rpx;
left: 50%;
transform: translateX(-50%);
display: flex;
align-items: center;
&>image{
width: 120rpx;
height: 120rpx;
}
.turn{
margin-left: 80rpx;
text-align: center;
image{
height: 35rpx;
width: 42rpx;
}
font-size: 28rpx;
color: #FFFFFF;
}
.time{
font-size: 38rpx;
color: #FFFFFF;
position: absolute;
top: -80%;
left: 50%;
transform: translateX(-50%);
}
}
.mongolian{
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #000000;
.colorBox{
position: absolute;
bottom: 200rpx;
left: 50%;
transform: translateX(-50%);
display: flex;
align-items: center;
justify-items: center;
.chooseColor{
border: 2rpx solid white;
}
view{
width: 63rpx;
height: 63rpx;
}
view:nth-of-type(1){
background-color: white;
}
view:nth-of-type(2){
background-color: #424242;
}
view:nth-of-type(3){
background-color: #000000;
}
view:nth-of-type(4){
background-color: #FA797D;
}
view:nth-of-type(5){
background-color: #FB6F24;
}
view:nth-of-type(6){
background-color: #F9D63C;
}
view:nth-of-type(7){
background-color: #77E46F;
}
view:nth-of-type(8){
background-color: #5A95EF;
}
view:nth-of-type(9){
background-color: #998BFC;
}
}
}
.editText{
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: white;
z-index: 123;
border-radius: 20rpx 20rpx 0 0;
.editTextTitle{
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx 20rpx;
border-bottom: 2rpx solid #D8D8D8;
text:nth-of-type(1){
font-size: 28rpx;
color: #666666;
}
text:nth-of-type(2){
font-size: 32rpx;
}
text:nth-of-type(3){
font-size: 28rpx;
color: #621AA4;
}
}
.editTextContent{
padding: 20rpx;
textarea{
width: 100%;
}
}
}
}
</style>