制作一个广告字幕滚动效果的网页的心路历程

先看效果

刚开始的需求是,制作一个类似电视广告字幕的效果,我没什么思路,后来看了字幕的实现方式,就是用css动画的移动实现,于是我开始了对这段动画的驾驭。

技术:万能的vue.js html5 css3

首先我们要提取到一个html文件中,在文件中引用需要的CDN

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>demo</title>
</head>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<style lang='scss' scoped>

</style>

<body>
    <div id="app">
        
    </div>
</body>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="https://code.jquery.com/jquery-3.6.0.slim.js"
    integrity="sha256-HwWONEZrpuoh951cQD1ov2HUK5zA5DwJ1DNUXaM6FsY=" crossorigin="anonymous"></script>
<script>
    new Vue({
        el: '#app',
        data: function () {
            return {
                
            }
        },
        mounted() {
            
        },
        methods: {
            
        }
    })
</script>

</html>

我们写一个外面的块,宽度是100%,再写一个里面的块,再里面的块里放置内容。内容部分暂且不管,我主要讲实现思路。

<body>
    <div id="app">
        <template>
            <div class="marquee-outer-wrapper">
                <div class="marquee-inner-wrapper">
                    <div class="span first-marquee">
                        <div class="item" v-for="item in cionList" :key="item.id">
                            <img :src="item.icon" alt="" class="icon" />
                            <div class="title">{{item.name}}</div>
                            <div class="text">[{{item.crypto}}]</div>
                            <div class="pre">${{item.a}}</div>
                            <div :class="item.isRed > 0 ? 'green' : 'red'">
                                <i
                                    :class="item.isRed > 0 ? 'el-icon-caret-top' : 'el-icon-caret-bottom'">{{item.isRed}}%</i>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </template>
    </div>
</body>
.marquee-outer-wrapper {
        width: 100%;
        overflow: hidden;
        position: absolute;
        top: 0;
        left: 0;
    }

    .marquee-inner-wrapper {
        background: #1D2330;
        height: 50px;
        line-height: 50px;
        margin: 0 auto;
        white-space: nowrap;
        position: relative;
    }

    .marquee-inner-wrapper .span {
        position: absolute;
        top: 0;
        left: 100%;
        /* right: 100%; */
        height: 100%;
        display: flex;
        flex-direction: row;
        justify-content: space-evenly;
    }

    .item {
        display: flex;
        flex-direction: row;
        justify-content: start;
    }

    .first-marquee {
        -webkit-animation: 20s first-marquee linear infinite normal;
        animation: 20s first-marquee linear infinite normal;
        padding-left: 20%;
    }

    .icon {
        width: 22px;
        height: 22px;
        margin-top: 14px;
        margin-left: 20px;
    }

    .title {
        line-height: 50px;
        font-size: 14px;
        color: #2D74C4;
        margin-left: 6px;
    }

    .text {
        line-height: 50px;
        font-size: 12px;
        color: #939494;
        margin-left: 4px;
    }

    .pre {
        line-height: 50px;
        font-size: 14px;
        color: white;
        font-weight: bold;
        margin-left: 6px;
    }

    .red {
        line-height: 50px;
        font-size: 10px;
        color: #E5541E;
        margin-left: 4px;
    }

    .green {
        line-height: 50px;
        font-size: 10px;
        color: #049E62;
        margin-left: 4px;
    }

    @keyframes first-marquee {
        0% {
            -webkit-transform: translate3d(0, 0, 0);
            transform: translate3d(0, 0, 0);
        }

        /* 向左移动 */
        100% {
            -webkit-transform: translate3d(-200%, 0, 0);
            transform: translate3d(-200%, 0, 0);
            display: none;
        }
    }

这里的思路主要是把里面的div放在屏幕的最右侧,然后设置向左迅速运动的动画,达到字幕滚动的效果。

但是问题来了,动画只运行一次,结束后才运行第二次。功能达到了但是效果不够完美。

于是,我想再写一个里面的div,但是是在第一个div先运行一段时间后再跑起来,思路有了,开始实现。

<body>
    <div id="app">
        <template>
            <div class="marquee-outer-wrapper">
                <div class="marquee-inner-wrapper">
                    <div class="span first-marquee">
                        <div class="item" v-for="item in cionList" :key="item.id">
                            <img :src="item.icon" alt="" class="icon" />
                            <div class="title">{{item.name}}</div>
                            <div class="text">[{{item.crypto}}]</div>
                            <div class="pre">${{item.a}}</div>
                            <div :class="item.isRed > 0 ? 'green' : 'red'">
                                <i
                                    :class="item.isRed > 0 ? 'el-icon-caret-top' : 'el-icon-caret-bottom'">{{item.isRed}}%</i>
                            </div>
                        </div>
                    </div>

                    <div class="span second-marquee">
                        <div class="item" v-for="item in cionList" :key="item.crypto">
                            <img :src="item.icon" alt="" class="icon" />
                            <div class="title">{{item.name}}</div>
                            <div class="text">[{{item.crypto}}]</div>
                            <div class="pre">${{item.a}}</div>
                            <div :class="item.isRed > 0 ? 'green' : 'red'">
                                <i
                                    :class="item.isRed > 0 ? 'el-icon-caret-top' : 'el-icon-caret-bottom'">{{item.isRed}}%</i>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </template>
    </div>
</body>
.second-marquee {
        -webkit-animation: 20s first-marquee linear 8s infinite normal;
        animation: 20s first-marquee linear 8s infinite normal;
        padding-left: 20%;
    }

在原有的基础上,又添加了一个类是span的div。不同的是它运行的动画是.second-marquee。这个动画和原有的动画区别就在于,这个动画相对上一个动画延时8s播放,达到一前一后的效果。

现在动画动起来了,但是需求又增加了。

大致内容就是在地址栏输入参数,根据参数的值可以修改一些属性,比如整体的高度,滚动条的颜色,运行的速度,运行的方向,甚至字体的大小。那么先来处理参数。

getParams() {
                let str = window.location.href
                if (str.indexOf("?") == -1) {
                    return
                } else {
                    let params = str.split('?')[1]
                    let paramsList = params.split('&')
                    let paramsObj = {}
                    for (let i = 0; i < paramsList.length; i++) {
                        const item = paramsList[i];
                        paramsObj[item.split('=')[0]] = item.split('=')[1]
                    }
                    // console.log(paramsObj);
                    this.params = paramsObj
                    this.size = this.params.size
                }
            }

这时,我们不知道传来的参数都是什么,除了size,因为是后加的,我就直接提取了出来。

那么接下来考虑的是怎么改这些属性。因为这些属性都是写在样式里面的,我首先想到vue和jquery混写,但是网上都说尽量避免这种写法。那就另辟蹊径,利用document.styleSheets获取当前文件的所有css,再利用deleteRule方法和insertRule方法修改。继续实现。

setStyle() {
                if (!this.params) {
                    return
                } else {
                    console.log(document.styleSheets[1]);
                    var style = document.styleSheets[1];
                    // 控制向左向右
                    if (!this.params.direction) {
                        return
                    } else if (this.params.direction == "left") {
                        style.deleteRule(2)
                        style.insertRule(".marquee-inner-wrapper .span { position: absolute; top: 0px; left: 100%; height: 100%; display: flex; flex-direction: row; justify-content: space-evenly; }", 2);
                        style.deleteRule(12)
                        style.insertRule("@keyframes first-marquee { \n  0% { transform: translate3d(0px, 0px, 0px); }\n  100% { transform: translate3d(-200%, 0px, 0px); display: none; }\n}", 12)
                    } else {
                        style.deleteRule(2)
                        style.insertRule(".marquee-inner-wrapper .span { position: absolute; top: 0px; right: 100%; height: 100%; display: flex; flex-direction: row; justify-content: space-evenly; }", 2);
                        style.deleteRule(12)
                        style.insertRule("@keyframes first-marquee { \n  0% { transform: translate3d(0px, 0px, 0px); }\n  100% { transform: translate3d(200%, 0px, 0px); display: none; }\n}", 12)
                    }
                    // 控制高度
                    if (!this.params.height) {
                        return
                    } else {
                        style.deleteRule(11)
                        style.insertRule(".green { line-height: " + this.params.height + "px; font-size: " + this.size * 0.7 + "px; color: rgb(4, 158, 98); margin-left: 4px; }", 11)
                        style.deleteRule(10)
                        style.insertRule(".red { line-height: " + this.params.height + "px; font-size: " + this.size * 0.7 + "px; color: rgb(229, 84, 30); margin-left: 4px; }", 10)
                        style.deleteRule(9)
                        style.insertRule(".pre { line-height: " + this.params.height + "px; font-size: " + this.size + "px; color: white; font-weight: bold; margin-left: 6px; }", 9)
                        style.deleteRule(8)
                        style.insertRule(".text { line-height: " + this.params.height + "px; font-size: " + this.size * 0.8 + "px; color: rgb(147, 148, 148); margin-left: 4px; }", 8)
                        style.deleteRule(7)
                        style.insertRule(".title { line-height: " + this.params.height + "px; font-size: " + this.size + "px; color: rgb(45, 116, 196); margin-left: 6px; }", 7)
                        style.deleteRule(6)
                        style.insertRule(".icon { width: " + this.size * 1.5 + "px; height: " + this.size * 1.5 + "px; margin-top: " + (this.params.height - this.size * 1.5) / 2 + "px; margin-left: 20px; }", 6)
                        style.deleteRule(1)
                        style.insertRule(".marquee-inner-wrapper { background: rgb(29, 35, 48); height: " + this.params.height + "px; line-height: " + this.params.height + "px; margin: 0px auto; white-space: nowrap; position: relative; }", 1)
                    }
                    // 控制背景颜色
                    if (!this.params.backgroundColor) {
                        return
                    } else {
                        document.getElementsByClassName("marquee-inner-wrapper")[0].style.backgroundColor = "rgb(" + this.params.backgroundColor + ")"
                    }
                    // 控制速度
                    if (!this.params.speed) {
                        return
                    } else {
                        style.deleteRule(4)
                        style.insertRule(".first-marquee { animation: " + this.params.speed + "s linear 0s infinite normal none running first-marquee; padding-left: 20%; }", 4)
                        style.deleteRule(5)
                        style.insertRule(".second-marquee { animation: " + this.params.speed + "s linear " + (this.params.speed) / 2 + "s infinite normal none running first-marquee; padding-left: 20%; }", 5)
                    }
                }
            }

这里应该是有优化的空间,我说一下我这种写法的坑。注意一下控制高度的地方,这里顺序是从高到低,为什么这样操作呢,因为当你使用deleteRule方法后,样式的数量会减少,下标也会随之变化,那为什么要用deleteRule方法呢,因为insertRule方法是插入一条,覆盖之前的,这样我们就先发制人,先删除再插入就ok了。

再来说一下insertRule后面跟的字符串就是这个样式的并行字符串,非常容易写错,怎么办呢,就是打印document.styleSheets,这里面每个样式都写的一清二楚,复制过来再把变量替换进去就ok啦。

现在是两列“小火车”交替行进,如何控制他们的距离呢,也就是两列小火车的距离,就在这里

.first-marquee {
        -webkit-animation: 0s first-marquee linear infinite normal;
        animation: 0s first-marquee linear infinite normal;
        padding-left: 20%;
    }

    .second-marquee {
        -webkit-animation: 0s first-marquee linear 0s infinite normal;
        animation: 0s first-marquee linear 0s infinite normal;
        padding-left: 20%;
    }

注意看这个padding-left: 20%;,你可以试试其他的值有什么意想不到的效果。

这里也是经历了我的魔改最后实现一个完美的效果。

最后再来看一遍成品

 最后的最后,这个数据接口还是自己找吧,网络上应该不少,或者使用假数据练习。

能读到这的都是大牛,哈哈哈哈我废话贼多


拓展

你觉得这种滚动效果可以做什么有趣的功能呢?

鼠标悬停可以暂停动画,配合点击事件,做一个小小的播放器怎么样。

放一些链接进去,点击之后在小窗里面显示网页内容。

还能怎么玩?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值