用 TypeScript + Vue.js 打造一個漸現 Banner 組件

漸現 Banner,也是輪播圖的一種。現在我們用 Vue.js 組件封裝它,而且是 TypeScipt 語法的。本組件不依賴其他庫或者函數。

用法如下:

<html>
	<head>
		<meta charset="utf-8" />
		<title>DEMO</title>
		<style type="text/css">
			/* AJAXJS Base CSS */
			body,dl,dt,dd,ul,li,pre,form,fieldset,input,p,blockquote,th,td,h1,h2,h3,h4,h5{margin:0;padding:0;}
			h1,h2,h3,h4,h5{font-weight: normal;}img{border:0;}ul li{list-style-type:none}.hide{display:none}
			body {-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing: grayscale;
				font-family: "Lantinghei SC", "Open Sans", Arial, "Hiragino Sans GB", "Microsoft YaHei", "微軟雅黑", "STHeiti", "WenQuanYi Micro Hei", SimSun, sans-serif;}
			a{text-decoration:none;color:#666;transition:color .4s ease-in-out;}
			a:hover{color:#000;}
			button{border:none;outline:0;cursor:pointer;letter-spacing:2px;text-align:center;-webkit-user-select:none;-moz-user-select:none;user-select:none}
			input[type=password],input[type=text],select,textarea{outline:0;-moz-appearance:none;}
			
			/* 手機端瀏覽器所顯示的網頁 CSS */
			@media screen and (max-width:480px) {
				* {
					-webkit-tap-highlight-color: transparent; /* 很多 Android 瀏覽器的 a 鏈接有邊框,這裏取消它  */
					-webkit-touch-callout: none; /* 在 iOS 瀏覽器裏面,假如用戶長按 a 標籤,都會出現默認的彈出菜單事件 */
					/* -webkit-user-select:none; */
				}
			}	
		</style> 
		<link rel="stylesheet" type="text/css" href="../common/main.css" />
		<script src="https://lib.baomitu.com/vue/2.6.11/vue.js"></script>
		<script src="../../dist/carousel/opacity-banner.js"></script>
	</head>
	<body>
		<div class="opacity-banner">
			<aj-opacity-banner>
				<li>
					<a href="#"> <img src="images/1.jpg" /></a>
				</li>
				<li>
					<a href="#"> <img src="images/2.jpg" /></a>
				</li>
				<li>
					<a href="#"> <img src="images/3.jpg" /></a>
				</li>
			</aj-opacity-banner>
		</div>

		<script type="text/javascript">
			new Vue({
				el: '.opacity-banner'
			});
		</script>
	</body>
</html>

<aj-opacity-banner> 包裹着的是含有圖片的 <li></li> 標籤。圖片是 100% 寬度自適應的,見下面所用到的樣式(基於 less.js)

.aj-opacity-banner{
	position: relative;
	
	li{
		position:absolute;
		top:0;
		left:0;
		opacity:0;
		width: 100%;
	}
	
	img{
		width: 100%;
	}
}

組件的屬性如下。

屬性 含義 類型 是否必填 默認值
delay 延時 Number n 3000
fps 幀速 Number n 25

用法如下:

<aj-opacity-banner delay="2000">
      ……
</aj-opacity-banner>

TypeScript 源碼如下:

/**
 * 漸顯 banner
 * 注意:定時器保存在 DOM 元素的屬性上,是否會內存泄漏呢?
 */
; (() => {
    interface OpacityBanner extends Vue {
        active: number;

        timer: number;

        delay: number;

        fps: number;

        /**
         * 各幀
         */
        list: NodeListOf<HTMLLIElement>;

        states: [];

        /**
        * 內容淡出 
        */
        clear(): void;

        animate(params: number): void;

        run(): void;
    }

    Vue.component('aj-opacity-banner', {
        template: '<ul class="aj-opacity-banner"><slot></slot></ul>',
        props: {
            delay: { default: 3000 },   // 延時
            fps: { default: 25 }        // 幀速
        },
        data() {
            return {
                active: 0,  // 當前索引
            }
        },
        mounted(this: OpacityBanner): void {
            this.list = this.$el.querySelectorAll('li');
            this.list[0].style.opacity = "1";
            console.log(this.list.length);
            this.run();
        },
        methods: {
            /**
             * 播放動畫
             * 
             * @param this 
             */
            run(this: OpacityBanner): void {
                this.timer = window.setInterval(() => {
                    var active: number = this.active;
                    this.clear();
                    active += 1;
                    this.active = active % this.list.length;
                    this.animate(100);
                }, this.delay);
            },

            /**
             * 下一幀
             * 
             * @param this 
             */
            per(this: OpacityBanner): void {
                var active = this.active;
                this.clear();
                active -= 1;
                active = active % this.list.length;

                if (active < 0)
                    active = this.list.length - 1;

                this.active = active;
                this.animate(100);
            },

            /**
             * 內容淡出 
             */
            clear(this: OpacityBanner): void {
                this.animate(0);
            },

            /**
             * 
             * @param this 
             * @param params 
             */
            animate(this: OpacityBanner, params: number): void {
                var el: HTMLLIElement = this.list[this.active], fps: number = 1000 / this.fps;
                window.clearTimeout(el.timer);

                window.setTimeout(function loop() {
                    var i: number = getOpacity(el);
                    var speed: number = (params - i) / 8, speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
                    // console.log("i=" + i + "; speed="+ speed+"; s="+s+"; k="+k);
                    i += speed;
                    el.style.opacity = String(i / 100);

                    window.clearTimeout(el.timer);
                    // params.complete && params.complete.call(elem);

                    el.timer = window.setTimeout(loop, fps);
                }, fps);
            }
        }
    });

    /**
     * 獲取元首的透明度
     * 
     * @param el 
     */
    function getOpacity(el: Element): number {
        var v: number = Number(getComputedStyle(el)["opacity"]);
        v *= 100;

        return parseFloat(v + "") || 0;
    }
})();

原理上講,就是爲每張圖片準備好定時器 timer,使其控制圖片的透明度。當圖片從透明度 0 到 100,就是漸現的過程;與此同時,另外一張圖片由透明度 100 下降到 0。這兩個過程是同時發生的,一個漸現一個漸隱,便會造成如此的目標效果。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章