目录
1.需求背景及重点需求和解决方式
2.效果实现
3.遇到的问题以及解决思路
需求背景:
需求的最终目的是为了让门店更了解参加活动的实时数据,通过可视化大屏的形式进行展现。此需求在两个项目上开发,唯一的区别就是门店端只会显示当前门店的数据信息,这篇博客我们主要介绍门店端的开发历程。
重点需求:
1.为了高效还原ui图,手写倒计时正计时组件,Switch组件;
2.订单金额没隔3位数加,号(可以参考我之前的博客);
3.二次封装组件,与后端建立长链接保持数据的实时通讯(大屏金额发生变化时数字跳 动效果&&实时弹幕);
4.使用rem布局适配大屏;
5.使用原生js实现屏幕百分百显示效果;
6.使用js完成一些特效;
效果实现:
1.手写组件
倒计时正计时组件:
<div class="active_nam">距离活动结束还剩</div>
<div class="countdown">
<div class="div-con">{{days.toString().padStart(2,0)}}</div><div class="div-dw">天</div>
<div class="div-con">{{hours.toString().padStart(2,0)}}</div><div class="div-dw">时</div>
<div class="div-con">{{minutes.toString().padStart(2,0)}}</div><div class="div-dw">分</div>
<div class="div-con">{{seconds.toString().padStart(2,0)}}</div><div class="div-dw">秒</div>
</div>
<div class="diachronic">
活动历时 : {{day.toString().padStart(2,0)}}:{{hour.toString().padStart(2,0)}}:{{minute.toString().padStart(2,0)}}:{{second.toString().padStart(2,0)}}
</div>
js:
calculateTheTime(row) {
// 此处获取时间信息完成倒计时单位转换
let start = new Date() ? dayjs(new Date()).format("YYYY/MM/DD HH:mm:ss") :""; // 当前最新的时间
let begin = row.startTime ? dayjs(row.startTime).format("YYYY/MM/DD HH:mm:ss") : "" // 开始时间
let end = row.endTime ? dayjs(row.endTime).format("YYYY/MM/DD HH:mm:ss") : "" // 结束时间
let start_num = new Date(start.replace(/-/g, "/"))
let end_num = new Date(end.replace(/-/g, "/"))
let begin_num = new Date(begin.replace(/-/g, "/"))
if(end_num.getTime() > start_num.getTime()){
this.countdown = parseInt(end_num.getTime() - start_num.getTime())
this.formatDuring(this.countdown)
this.diachronic = parseInt(start_num.getTime() - begin_num.getTime())
const Duration = parseInt(end_num.getTime() - begin_num.getTime())
this.elapsedTime(this.diachronic,Duration)
}else{
this.diachronic = parseInt(end_num.getTime() - begin_num.getTime())
const Duration = parseInt(end_num.getTime() - begin_num.getTime())
this.elapsedTime(this.diachronic,Duration)
}
},
formatDuring(millisecond) { // 手写倒计时 倒计时时间为0000的时候清除计时器
this.days = parseInt(millisecond / (1000 * 60 * 60 * 24));
this.hours = parseInt((millisecond % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
this.minutes = parseInt((millisecond % (1000 * 60 * 60)) / (1000 * 60));
this.seconds = ((millisecond % (1000 * 60)) / 1000);
this.timeID = setInterval(()=>{
if(this.seconds>0){
this.seconds --
}else if(this.minutes>0){
this.seconds = 59
this.minutes --
}
else if(this.hours>0){
this.minutes = 59
this.hours --
}
else if(this.days>0){
this.hours = 23
this.days --
}
else if(this.days==0){
clearInterval(this.timeID)
}
},1000)
},
css:
.active_nam{
margin-top: 0.625rem;
font-size: 1.3125rem;
color: #FFF;
}
.countdown{
display: flex;
margin-top: 1.875rem;
.div-con{
font-size: 1.25rem;
font-weight: 600;
width: 2.8125rem;
height: 2.8125rem;
color: #20209D;
background-color: #FFF;
border-radius: 0.3125rem;
display: flex;
justify-content: center;
align-items: center;
}
.div-dw{
font-size: 1.3125rem;
color: #FFF;
width: 2.8125rem;
height: 2.8125rem;
border-radius: 0.3125rem;
display: flex;
justify-content: center;
align-items: center;
}
}
.diachronic{
margin-top: 1.25rem;
font-size: 1.3125rem;
color: #FFF;
}
Switch组件(主要借助伪元素实现效果):
<div class="switch-con" @click="barrageIsShow = !barrageIsShow">
<div class="outer">
<input type="checkbox" :checked="barrageIsShow">
<div class="slider"></div>
</div>
</div>
css:
.switch-con{
margin-top: 1rem;
width: 3.8rem;
height: 2rem;
border-radius: 1.5625rem;
padding: 0.187rem 0 0 0.125rem;
border: 0.0125rem solid transparent;
background-clip: padding-box, border-box;
background-origin: padding-box, border-box;
background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(33, 31, 48, 0.58))), -webkit-gradient(linear, left top, right top, from(rgba(193, 99, 152, 0.44)), to(rgba(176, 175, 242, 0.58)));
.outer{
width:3.375rem;
height:1.5rem;
position: relative;
.slider{
position: absolute;
left:0;
top:0;
right:0;
bottom:0;
background:rgba(33, 31, 48, 0.58);
border-radius: 1.125rem;
transition: 0.4s;
cursor: default;
line-height: 1.625rem;
&::before{
content: '';
position: absolute;
width:1.62rem;
height:1.62rem;
left:0;
top:-0.09rem;
border-radius: 50%;
transition: 0.4s;
box-sizing: border-box;
border: 0.0625rem solid transparent;
background-clip: padding-box, border-box;
background-origin: padding-box, border-box;
background-image: -webkit-gradient(linear, left top, left bottom, from(#CF48A9)), -webkit-gradient(linear, left top, right top, from(rgba(116, 30, 79, 1)), to(rgba(255, 255, 255, 0.44)));
}
}
input{
display: none;
&:checked + .slider{
color: #fff;
box-sizing: border-box;
border: 0.0625rem solid transparent;
background-clip: padding-box, border-box;
background-origin: padding-box, border-box;
background-image: -webkit-gradient(linear, left top, left bottom, from(#F487CD)), -webkit-gradient(linear, left top, right top, from(rgba(61, 12, 31, 0.85)), to(rgba(255, 255, 255, 0.24)));
}
&:checked + .slider::before{
left: 1.75rem;
}
}
}
}
2.借助 countTo 和 vueBaberrage 组件实施数字跳动以及弹幕效果
关于这两个组件的使用就不过多介绍大家自行去搜索也很简单
重点在于建立长链接生成一个唯一key后在把key储存给后端完成链接后通过vux加上侦听器实时监听数据变化并完成css效果,一下是部分代码:
长链接的js文件中:
import client from "./client"
const push = client();
// 这里的client 需要借助 @stomp/stompjs
export function initKey(shopcode) {
let str = shopcode + "-" + Math.uuid();
yid.cache.set("pushkey", str);
push.connect("wss://xxxxx.com/ws" , shopcode + "|" + str , async function (frame) {
let {webdata,memberBuyInfo} = JSON.parse(frame);
if(webdata)
Store.commit("update/setTimely",webdata);
if(memberBuyInfo)
Store.commit("update/setBuyInfo",memberBuyInfo);
}, function (frame) {
console.error('disconnect', frame);
});
}
在要是用的组件中侦听:
"$store.state.update.webdata": function () { // 获取vuex中数据并操作
// console.log(this.$store.state.update.webdata,'setTimely徐飞');
this.refreshData()
this.data = this.$store.state.update.webdata
this.startVal = this.endVal // 保证第二次变化的初始值
this.endVal = this.data.ordersMoney?this.data.ordersMoney:0
},
"$store.state.update.memberBuyInfo": function () { // 获取vuex中数据并操作
// console.log(this.$store.state.update.memberBuyInfo,'memberBuyInfo徐飞');
let memberBuyInfo = this.$store.state.update.memberBuyInfo
this.addToList(memberBuyInfo)
}
弹幕样式更改:
/*弹幕区域高度*/
/deep/ .baberrage-stage {
padding: 12.5rem 2rem;
width: 87.5rem;
height: 62.5rem;
}
/*弹幕消息框*/
/deep/ .baberrage-item .normal {
padding: 0 1.25rem;
background: none;
}
// /*头像*/
/deep/ .baberrage-item .normal .baberrage-avatar {
width: 2.875rem;
height: 2.875rem;
border-radius: 50%;
border: 0.0625rem solid transparent;
background-clip: padding-box, border-box;
background-origin: padding-box, border-box;
background-image: linear-gradient(rgba(193, 99, 152, 0.44)), linear-gradient(to right, rgba(193, 99, 152, 0.44), rgba(176, 175, 242, 0.58));
img{
width: 2.875rem;
height: 2.875rem;
border-radius: 50%;
}
}
// /*文字*/
/deep/ .baberrage-item .normal .baberrage-msg {
height: 2rem;
margin-top: 0.4375rem;
margin-left: -0.5rem;
font-size: 1.125rem;
line-height: 2rem;
background: rgba(0,0,0,0.2)!important;
border-radius:0 1.5625rem 1.5625rem 0;
border: 0.0563rem solid rgba(193, 99, 152, 0.44);
border-left: none;
padding: 0 1.25rem;
}
3.原生js实现大屏效果(并改变fontsize大小完成rem自适应):
handleFullScreen (params) {
// 实现全屏的方法
function requestFullScreen(element) {
var requestMethod = element.requestFullScreen || //W3C
element.webkitRequestFullScreen || //Chrome
element.mozRequestFullScreen || //FireFox
element.msRequestFullScreen; //IE11
if (requestMethod) {
requestMethod.call(element);
} else if (typeof window.ActiveXObject !== "undefined") { //for Internet Explorer
var wscript = new ActiveXObject("WScript.Shell");
if (wscript !== null) {
wscript.SendKeys("{F11}");
}
}
}
requestFullScreen(document.documentElement);
let doc = document.getElementsByClassName('Content')[0]
let bgimg = document.getElementsByClassName('bg-img')[0]
doc.style.position = 'fixed'
doc.style.left = 0
doc.style.top = 0
doc.style.width = '100vw'
bgimg.style.height = '100vh'
window.onresize = function () {
let doc = document.getElementsByClassName('Content')[0]
if (!doc) { return }
if (document.fullscreenElement) {
console.log('进入全屏')
if(doc.offsetWidth > 1430) // 徐飞对单页面进行rem适配
document.documentElement.style.fontSize=doc.offsetWidth/105+"px"
// console.log(document.documentElement.style.fontSize,'doc.offsetWidth') // 太强了
} else {
// 退出全屏的时候恢复原来的样式
console.log('退出全屏')
doc.style.position = 'relative'
doc.style.left = 'inherit'
doc.style.top = 'inherit'
doc.style.width = 'inherit'
bgimg.style.height = 'inherit'
document.documentElement.style.fontSize = 'inherit'
}
};
},
exitFull() { //退出全屏 esc键和F11可以直接退出,
// 判断各种浏览器,找到正确的方法
var exitMethod = document.exitFullscreen || //W3C
document.mozCancelFullScreen || //FireFox
document.webkitExitFullscreen || //Chrome等
document.webkitExitFullscreen; //IE11
if (exitMethod) {
exitMethod.call(document);
} else if (typeof window.ActiveXObject !== "undefined") { //for Internet Explorer
var wscript = new ActiveXObject("WScript.Shell");
if (wscript !== null) {
wscript.SendKeys("{F11}");
}
}
},
遇到的问题:
1.子组件使用v-show后里面调用弹幕组件会失效
解决办法:使用路由加载数据实时大屏。顺便解决了大屏之前f11后丑陋的问题
其他还好都解决了。