萌新探路,各位大佬见笑。
首先贴上常见的一种方法:
v1.0
<div id="box">
<ul id="ul">
<li>第一条</li>
<li>第二条</li>
<li>第三条</li>
<li>第四条</li>
</ul>
<ul>
<li>第一条</li>
<li>第二条</li>
<li>第三条</li>
<li>第四条</li>
</ul>
</div>
* {
margin: 0;
padding: 0;
}
div {
width: 100px;
height: 63px; /* 必须 */
overflow: hidden; /* 必须 */
margin: 20% auto;
border: 1px solid red;
text-align: center;
ul {
list-style: none;
}
ul li {
background-color: aqua;
box-sizing: border-box;
border: 1px solid #000;
}
}
box: any;
ul: any;
ngAfterViewInit(): void {
this.box = document.getElementById('box');
this.ul = document.getElementById('ul');
this.roll(50);
}
roll(t: any): void {
this.box.scrollTop = 0; // 开始无滚动时设为0
let timer = setInterval(this.rollStart, t); // 设置定时器,参数t用在这为间隔时间(单位毫秒),参数t越小,滚动速度越快
// 鼠标移入div时暂停滚动
this.box.onmouseover = () => {
clearInterval(timer);
};
// 鼠标移出div后继续滚动
this.box.onmouseout = () => {
timer = setInterval(this.rollStart, t);
};
}
// 开始滚动函数
rollStart(): void {
// 正常滚动不断给scrollTop的值+1,当滚动高度大于列表内容高度时恢复为0
if (this.box.scrollTop >= this.ul.scrollHeight) {
this.box.scrollTop = 0;
} else {
this.box.scrollTop++;
}
}
到目前为止,上述写法看起来正常,功能也实现了。但是一般来说html里的ul li是不会像上方这么写的,而是使用ngFor来对li进行循环输出,所以我在这里对html代码进行进一步的优化。
<div #scroller>
<div #scrollItem>
<div *ngFor="let item of list">
{{item}}
</div>
</div>
<div #scrollAdd></div>
</div>
然后我们在ts中加上以下代码:
@ViewChild('scroller', { static: false }) scroller: ElementRef;
@ViewChild('scrollItem', { static: false }) scrollItem: ElementRef;
@ViewChild('scrollAdd', { static: false }) scrollAdd: ElementRef;
// roll 方法中添加
const scrollItem = this.scrollItem.nativeElement;
const scrollAdd = this.scrollAdd.nativeElement;
scrollAdd.innerHTML = scrollItem.innerHTML;
乍一看没有什么问题了,但是运行后发现scrollItem的innerHTML值为空,也就是说在innerHTML这段代码执行的时候,实际上使用ngFor指令的li们还没有渲染出来。
就目前而言我只想到了一种方法,有大佬会其他方法的还望赐教。
我给出的解决方案是:使用自定义指令。
下面贴出我的完整解决代码:
v2.0
<div #scroller>
<div #scrollItem>
<div
*ngFor="let item of list; let last = last"
[afterFinish]="last"
appAfterFinish
(ngAfterFinish)="rollBefore($event)"
>
{{item}}
</div>
</div>
<div #scrollAdd></div>
</div>
@ViewChild('scroller', { static: false }) scroller: ElementRef;
@ViewChild('scrollItem', { static: false }) scrollItem: ElementRef;
@ViewChild('scrollAdd', { static: false }) scrollAdd: ElementRef;
...
/** 数据加载完毕后进行滚动 */
rollBefore(event: any): void {
if (event) {
this.roll(50);
}
}
/** 滚动准备 */
roll(t: any): void {
const father = this.scroller.nativeElement;
const scrollItem = this.scrollItem.nativeElement;
const scrollAdd = this.scrollAdd.nativeElement;
scrollAdd.innerHTML = scrollItem.innerHTML;
father.scrollTop = 0; // 开始无滚动时设为0
let timer = setInterval(this.rollStart.bind(this), t); // 设置定时器,参数t用在这为间隔时间(单位毫秒),参数t越小,滚动速度越快
// 鼠标移入div时暂停滚动
father.onmouseover = () => {
clearInterval(timer);
};
// 鼠标移出div后继续滚动
father.onmouseout = () => {
timer = setInterval(this.rollStart.bind(this), t);
};
}
/** 开始滚动 */
rollStart(): void {
const father = this.scroller.nativeElement;
const scrollItem = this.scrollItem.nativeElement;
// 正常滚动不断给scrollTop的值+1,当滚动高度大于列表内容高度时恢复为0
if (father.scrollTop >= scrollItem.scrollHeight) {
father.scrollTop = 0;
} else {
father.scrollTop++;
}
}
自定义指令:
import {
Directive,
Input,
EventEmitter,
Output,
AfterViewInit,
} from '@angular/core';
@Directive({
selector: '[appAfterFinish]',
})
export class AfterFinishDirective implements AfterViewInit {
@Input() afterFinish: boolean;
@Output() ngAfterFinish: EventEmitter<boolean> = new EventEmitter();
constructor() {}
ngAfterViewInit(): void {
if (this.afterFinish) {
this.ngAfterFinish.emit(this.afterFinish);
}
}
}
我的切入点是ngFor指令向外暴露了一个last变量,这个变量的作用是获取ngFor渲染的最后一个元素。在ngFor进行渲染的过程中,如果还未渲染完成,last的值为false,渲染完成值为true,我们便可以利用last的特性,对其进行监听,等其值为true是,在进行innerHTML值的相关操作,在这里我使用了自定义指令来进行last为true之后的相关操作,由于操作时放在ngAfterViewInit里的,所以此时的ngFor已然渲染完毕。
以上。