渐现 Banner,也是轮播图的一种。现在我们用 Vue.js 组件封装它,而且是 TypeScript 语法的。本组件不依赖其他库或者函数。
用法如下:
<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。这两个过程是同时发生的,一个渐现一个渐隐,便会造成如此的目标效果。