This commit is contained in:
2024-05-17 18:02:49 +08:00
parent 8407d51fb6
commit b5264dc222
4056 changed files with 308094 additions and 41932 deletions

View File

@@ -0,0 +1,244 @@
<template>
<view
v-if="showFixedFooter"
class="fixed-footer"
:class="[isFullScreen ? 'full-screen' : '', NotchClass]">
<view class="place-holder" @click="handleToggleFixedFooter"></view>
<view
class="barrage-input-bar">
<image
v-if="pickColorMode"
class="color-btn"
src="@/static/barrage/color_active@2x.png"
@click="togglePickColorMode"></image>
<image
v-else
class="color-btn"
:src="isFullScreen ? require('@/static/barrage/color_white@2x.png') : require('@/static/barrage/color_gray@2x.png')"
@click="togglePickColorMode"></image>
<view class="input-wrapper">
<input
ref="barrageFormInput"
class="input"
type="text"
v-model="barrageText"
adjust-position="false"
placeholder="弹幕走一波"
placeholder-style="font-size:13px;color:#999;"
cursor-spacing="10px"
maxlength="30"
@focus="handleFocus"
@blur="handleBlur"
@keyboardheightchange="handleKeyboardHeightChange">
<text class="total-label">{{ restLength }}</text>
</view>
<text
class="send-btn"
@click="handleBarrageSend"
>发送</text>
</view>
<view
v-if="keyboardHeight"
class="barrage-color-bar"
:style="{height: keyboardHeight + 'px'}">
<text class="title">弹幕颜色</text>
<view class="lists">
<view
class="color-item"
:class="activeColorIndex === index ? 'active' : ''"
v-for="(color, index) in barrageColors">
<view
class="color-box"
:style="{backgroundColor: color}"
@click="handleColorPick(index)"></view>
</view>
</view>
</view>
</view>
</template>
<script>
const { platform, safeArea, screenHeight, screenWidth } = uni.getSystemInfoSync();
//const hasNotchInScreen = plus.navigator.hasNotchInScreen();
export default {
data () {
return {
hasNotchInScreen,
barrageText: '' ,// 弹幕输入框文字
keyboardHeight: 0 // 软键盘高度
}
},
props: {
isFullScreen: {
type: Boolean,
default: false
},
showFixedFooter: {
type: Boolean
},
pickColorMode: {
type: Boolean,
default: false
},
barrageColors: {
type: Array,
default: []
},
activeColorIndex: {
type: Number,
default: 0
}
},
computed: {
restLength () {
return this.barrageText.length < 30 ? (30 - this.barrageText.length) : 0
},
NotchClass () {
return this.hasNotchInScreen ? 'notch' : '';
}
},
watch: {
showFixedFooter: function(val) {
if (!val) {
this.keyboardHeight = 0;
}
},
// fix: 安卓横屏不触发keyboardheightchange
isFullScreen: function(val) {
if (val) {
this.handleKeyboardHeightChange();
}
}
},
methods: {
focusAction () {
console.log('弹幕输入框focus');
let tag = false;
this.$refs.barrageFormInput.focus();
// 重新计算软键盘高度
this.handleKeyboardHeightChange();
},
blurAction () {
console.log('弹幕输入框blur');
this.$refs.barrageFormInput.blur();
},
cleanTextAction () {
this.barrageText = '';
},
handleFocus () {
this.$emit('handleFocus');
},
handleBlur () {
this.$emit('handleBlur');
},
handleKeyboardHeightChange (e) {
console.log('KeyboardHeightChange', e);
if (e && e.detail && !e.detail.height) return;
if (platform === 'ios') {
this.keyboardHeight = this.isFullScreen ? e.detail.height : (e.detail.height - safeArea.top + 15);
} else {
this.keyboardHeight = this.isFullScreen ? 207 : e.detail.height;
}
console.log('keyboradHeihgt:', this.keyboardHeight);
},
togglePickColorMode () {
// 安卓横屏禁用
if (platform === 'android' && this.isFullScreen) return;
this.$emit('togglePickColorMode');
},
handleBarrageSend () {
this.$emit('handleBarrageSend', this.barrageText);
},
handleColorPick (index) {
this.$emit('handleColorPick', index);
},
handleToggleFixedFooter () {
this.$emit('handleToggleFixedFooter');
}
}
}
</script>
<style lang="stylus" scoped>
.fixed-footer
position: fixed
top: 0
bottom: 0
left: 0
right: 0
&.full-screen
&.notch
.barrage-input-bar
padding: 0 88rpx
.barrage-color-bar
padding: 40rpx 88rpx
.barrage-input-bar
background-color: #27282A
.input-wrapper
background-color: #FFFFFF
.barrage-color-bar
background-color: #3D3D3F
.title
color: #FFFFFF
.place-holder
border-bottom: 1px solid #444;
.place-holder
flex: 1;
border-bottom: 1px solid #DDD;
.barrage-input-bar
padding: 0 30rpx
height: 90rpx
flex-direction: row
align-items: center
background-color: #FFF
box-shadow: 0px 1px 0px 0rpx #DDDDDD, 0px -1px 0px 0px #DDDDDD;
.color-btn
width: 60rpx
height: 60rpx
.input-wrapper
flex 1
flex-direction: row
align-items: center
margin: 0 20rpx
padding: 0 30rpx
height: 70rpx
background-color: #f5f5f5
border-radius: 100rpx
.input
flex 1
font-size: 26rpx
.total-label
font-size: 24rpx
color #999
.send-btn
width: 112rpx;
height: 60rpx;
line-height: 60rpx
background: #FF9520;
border-radius: 32rpx;
font-size: 30rpx
color: #fff
text-align: center
.barrage-color-bar
padding: 40rpx 60rpx
background-color: #F5F5F5
.title
font-size: 24rpx
color: #666
.lists
padding: 25rpx 9rpx
flex-direction: row
flex-wrap: wrap
.color-item
justify-content: center
align-items: center
margin: 15rpx 21rpx
padding: 5rpx
border-radius: 100%
&.active
border: 2rpx solid #FF920A;
.color-box
width: 44rpx
height: 44rpx
border-radius: 100%
</style>

View File

@@ -0,0 +1,103 @@
<template>
<view class="barrage-control-bar">
<view class="left">
<template v-if="barrageOpen">
<image
class="btn"
src="@/static/barrage/open@2x.png"
@click="handleToggleBarrageOpen"></image>
<image
v-if="settingBarrageMode"
class="btn space"
src="@/static/barrage/setting_active@2x.png"
@click="handleToggleBarrageSetting"></image>
<image
v-else
class="btn space"
src="@/static/barrage/setting_gray@2x.png"
@click="handleToggleBarrageSetting"></image>
</template>
<image
v-else
class="btn"
src="@/static/barrage/close_gray@2x.png"
@click="handleToggleBarrageOpen"></image>
</view>
<view
v-if="barrageOpen"
class="right"
@click="handleToggleFixedFooter">
<text class="text">{{ placeHolderText }}</text>
</view>
</view>
</template>
<script>
export default {
name: 'BarrageControlBar',
props: {
barrageOpen: {
type: Boolean,
default: true
},
settingBarrageMode: {
type: Boolean,
default: false
},
barrageSendDisabled: {
type: Boolean
},
barrageSendDisabledTime: {
type: Number
}
},
computed: {
placeHolderText () {
return this.barrageSendDisabled ? this.barrageSendDisabledTime + 's' : '点我发弹幕';
}
},
methods: {
handleToggleBarrageOpen () {
this.$emit('handleToggleBarrageOpen');
},
handleToggleBarrageSetting () {
this.$emit('handleToggleBarrageSetting');
},
handleToggleFixedFooter () {
this.$emit('handleToggleFixedFooter');
}
}
}
</script>
<style lang="stylus" scoped>
.barrage-control-bar
flex-direction: row
padding: 10rpx 30rpx
background-color: #FFF
.left
flex-direction: row
align-items: center
padding: 0 20rpx
background-color: #F5F5F5
border-radius: 30rpx
border 1rpx solid #DDDDDD
.btn
width: 60rpx
height: 60rpx
&.space
margin-left: 40rpx
.right
flex 1
flex-direction: row
align-items: center
margin-left: 20rpx
padding: 0 30rpx
background-color: #F5F5F5
border-radius: 30rpx
border 1rpx solid #DDDDDD
.text
font-size: 26rpx
line-height: 26rpx
color #999
</style>

View File

@@ -0,0 +1,267 @@
<template>
<!-- 弹幕设置弹层 -->
<view
v-if="settingBarrageMode"
class="barrage-setting-layer"
:class="[isFullScreen ? 'full-screen' : '', NotchClass]">
<view class="place-holder" @click="handleToggleBarrageSetting"></view>
<view class="layer">
<view class="header">
<image
v-if="isFullScreen"
class="back-btn"
src="@/static/barrage/back@2x.png"
@click="handleToggleBarrageSetting"></image>
<text class="title">弹幕设置</text>
<image
v-if="!isFullScreen"
class="close-btn"
src="@/static/barrage/close_setting@2x.png"
@click="handleToggleBarrageSetting"
></image>
</view>
<view class="content">
<view class="setting-item">
<text class="item-label">不透明度</text>
<view class="item-slider">
<custom-slider
class="slider"
:value="barrageOpacity"
@change="handleSetBarrageOpacity"></custom-slider>
<text class="item-value">{{ barrageOpacity }}%</text>
</view>
</view>
<view class="setting-item">
<text class="item-label">字号</text>
<view class="item-slider">
<custom-slider
class="slider"
:max="maxFontSize"
:min="minFontSize"
:step="fontSizeStep"
:value="barrageFontSize"
@change="handleSetBarrageFontSize"
></custom-slider>
<text class="item-value">{{ barrageFontSizeLabel }}</text>
</view>
</view>
<view class="setting-item">
<text class="item-label">速度</text>
<view class="item-slider">
<custom-slider
class="slider"
:step="speedStep"
:value="barrageSpeedValue"
@change="handleSetBarrageSpeed"></custom-slider>
<text class="item-value">{{ barrageSpeedLabel }}</text>
</view>
</view>
<view class="setting-item">
<text class="item-label">显示区域</text>
<view class="item-slider">
<custom-slider
class="slider"
:value="barrageArea"
@change="handleSetBarrageArea"
></custom-slider>
<text class="item-value">{{ barrageArea }}%</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import CustomSlider from '@/components/player/CustomSlider.nvue'
//const hasNotchInScreen = plus.navigator.hasNotchInScreen();
const fontSizes = [
{
label: '超小',
value: 16
},
{
label: '小',
value: 22
},
{
label: '正常',
value: 28
},
{
label: '大',
value: 34
},
{
label: '超大',
value: 40
}
]
const speeds = [
{
label: '超慢',
value: 17000
},
{
label: '慢',
value: 14000
},
{
label: '正常',
value: 10000
},
{
label: '快',
value: 6000
},
{
label: '超快',
value: 3000
}
]
export default {
components: {
CustomSlider
},
data () {
return {
hasNotchInScreen,
speedStep: 25
}
},
props: {
isFullScreen: {
type: Boolean,
default: false
},
settingBarrageMode: {
type: Boolean,
default: false
},
barrageOpacity: {
type: Number,
default: 100
},
barrageFontSize: {
type: Number,
default: 28
},
barrageSpeed: {
type: Number,
default: 10000
},
barrageArea: {
type: Number,
default: 100
}
},
computed: {
maxFontSize () {
return fontSizes[fontSizes.length - 1].value
},
minFontSize () {
return fontSizes[0].value
},
fontSizeStep () {
return (this.maxFontSize - this.minFontSize) / (fontSizes.length - 1)
},
barrageFontSizeLabel () {
const barrageFontSize = fontSizes.find(item => item.value === this.barrageFontSize);
return barrageFontSize.label
},
barrageSpeedLabel () {
const barrageSpeed = speeds.find(item => item.value === this.barrageSpeed);
return barrageSpeed.label
},
barrageSpeedValue () {
const barrageSpeedIndex = speeds.findIndex(item => item.value === this.barrageSpeed);
return barrageSpeedIndex * this.speedStep
},
NotchClass () {
return this.hasNotchInScreen ? 'notch' : '';
},
},
methods: {
handleToggleBarrageSetting () {
this.$emit('handleToggleBarrageSetting');
},
handleSetBarrageOpacity (value) {
this.$emit('handleSetBarrageOpacity', value);
},
handleSetBarrageFontSize (value) {
this.$emit('handleSetBarrageFontSize', value);
},
handleSetBarrageSpeed (value) {
const activeSpeedIndex = value / this.speedStep;
value = speeds[activeSpeedIndex].value
console.log(value);
this.$emit('handleSetBarrageSpeed', value);
},
handleSetBarrageArea (value) {
this.$emit('handleSetBarrageArea', value);
}
}
}
</script>
<style lang="stylus" scoped>
.barrage-setting-layer
position: fixed
top: 0
bottom: 0
right: 0
left: 0
&.full-screen
flex-direction: row
.layer
width 500rpx
.content
flex 1
&.notch
.layer
width: 588rpx;
.header
padding: 0 108rpx 0 20rpx
.content
padding-right: 88rpx
.place-holder
flex: 1
.layer
align-self: stretch
.header
flex-direction: row
align-items: center
height: 80rpx
padding: 0 20rpx
background-color: #141414
.title
flex: 1
font-size: 28rpx
color: #FF920A
.back-btn, .close-btn
width: 56rpx
height: 56rpx
.content
justify-content: space-around
height: 638rpx
background-color: rgba(20, 20, 20, 0.8)
.setting-item
padding: 0 30rpx
.item-label
font-size: 24rpx
color #FFFFFF
.item-slider
flex-direction: row
align-items: center
.slider
flex 1
.item-value
min-width: 72rpx
font-size: 24rpx
color #FFFFFF
</style>

View File

@@ -0,0 +1,125 @@
<template>
<view class="box">
<template v-for="item in lists">
<view
:ref="item.doomId"
class="box-item"
:style="{top:item.top+'px', opacity: barrageOpacity / 100 }">
<view
:class="[item.self ? 'self': '']">
<text :style="{color: `#${item.fc.substr(2)}`, fontSize: barrageFontSize + 'rpx'}">{{item.content}}</text>
</view>
</view>
</template>
</view>
</template>
<script>
const animation = uni.requireNativePlugin('animation')
export default {
name: "barrage",
data() {
return {
lists: [],
idx: 0,
};
},
props: {
playerHeight: {
type: Number
},
barrageOpacity: {
type: Number,
default: 100
},
barrageFontSize: {
type: Number,
default: 18
},
barrageSpeed: {
type: Number,
default: 10000
},
barrageArea: {
type: Number,
default: 100
}
},
methods: {
//添加弹幕测试doomData = {text: '6666666'}
add(doomData) {
this.addNvue(doomData)
},
addNvue(doomData) {
let that = this
this.idx++
let doomId = this.idx
// todo 弹幕区域调整
// let top = Math.ceil(Math.random() * this.playerHeight)
let top = this.dealTop(this.playerHeight, this.barrageArea);
doomData.doomId = doomId
doomData.top = top
// console.log("接收到的弹幕数据", doomData)
this.lists.push(doomData)
this.$nextTick(function(e) {
setTimeout(function(e) {
that.move(doomData)
}, 200)
})
},
dealTop (height, area) {
let column = Math.ceil(area / 25);
let randomColumn = Math.floor(Math.random() * column);
return Math.ceil(height * randomColumn / 4);
},
//nvue原生页面弹幕移动
move(doomData) {
let that = this
let doomId = doomData.doomId
let el = this.$refs[doomId][0]
// console.log("弹幕节点信息", el)
animation.transition(el, {
styles: {
transform: 'translateX(-100%)',
},
duration: this.barrageSpeed, //ms
timingFunction: 'linear', //匀速移动
delay: 0 //ms
}, () => {
//删除对应的弹幕数据,实测会导致渲染闪烁,如有需要自行优化
// that.lists.splice(that.lists.indexOf(doomData), 1)
// console.log("弹幕移动结束")
})
},
clean() {
this.lists = [];
}
}
}
</script>
<style lang="stylus" scoped>
.box
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
white-space: nowap;
.box-item
position: absolute;
right: -88rpx;
width: 750px;
margin-top: 20rpx;
flex-direction: row;
transform: 'translateX(100%)';
transformOrigin: 'left';
timingFunction: 'linear';
.self
padding: 10rpx;
border-radius: 24rpx;
border: 1rpx solid #FFFFFF;
background-color: rgba(102, 102, 102, 0.3);
</style>