0%

Angular-Carousel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import { Component, ContentChild, Input, TemplateRef } from "@angular/core";

@Component({
selector:'app-partner-carousel',
template:`
<div class="carousel">
<div class="btn-pre" (click)="carouselMove('LEFT')" [hidden]="carouselIndex===0"></div>
<div class="slick-window">
<div class="slick-container" *ngIf="data.length>0" [ngStyle]="carouselAnimotion">
<span *ngFor="let dataItem of data">
<ng-container
*ngIf="dataItemTemplateRef"
[ngTemplateOutlet]="dataItemTemplateRef"
[ngTemplateOutletContext]="{$implicit:dataItem}"></ng-container>
</span>
</div>
</div>
<div class="btn-next" (click)="carouselMove('RIGHT')" [hidden]="carouselIndex>=data.length-3"></div>
</div>
`,
styles:[`.carousel{display:flex}`,
`.slick-window {
position: relative;
width: 690px;
overflow: hidden;
transform: translateZ(0);
}`,
`.slick-container{
transform: translate3d(0px, 0px, 0px);
transition: 0.6s ease-in-out;
// width: 1500px;
position: relative;
line-height: inherit;
margin-right: 45px;
overflow-y: hidden;
}`,
`.slick-container>span{display:inline-block}`,
`.btn-pre, .btn-next {
// width: 15px;
cursor: pointer;
}`,
`.btn-pre::before, .btn-next::before {
content: '';
width: 0;
height: 0;
position: absolute;
top: 16px;
border-top: 22px solid transparent;
border-bottom: 22px solid transparent;
}`,
`.btn-pre::before {
border-right: 15px solid #ccc;
border-left: 0px solid transparent;
left: -15px;
}`,
`.btn-next::before {
border-right: 0px solid transparent;
border-left: 15px solid #ccc;
}`]
})
export class PartnerCarouselComponent{
readonly slickWidth = 230;
carouselAnimotion:any = {};
private _data:Array<any> = [];
@Input()
set data(value:Array<any>){
this.carouselAnimotion.width = `${this.slickWidth * value.length}px`;
this._data = value;
}
get data(){
return this._data;
}
@ContentChild('dataItem', {static:false}) dataItemTemplateRef:TemplateRef<any>;
carouselIndex:number = 0;
constructor(){}

carouselMove(orient: string) {
let distance = 0;
if (orient === 'LEFT') {
this.carouselIndex = this.carouselIndex > 3 ? this.carouselIndex -= 3 : 0
}
if (orient === 'RIGHT') {
this.carouselIndex = this.carouselIndex < this._data.length - 5 ? this.carouselIndex += 3 : this._data.length - 3;
}
distance = this.slickWidth * this.carouselIndex;
this.carouselAnimotion.transform = `translate3d(-${distance}px, 0px, 0px)`;
}
}

滑动原理

position: relative和transform: translateZ(0)将‘窗口’定位,其子元素是宽度超过屏幕宽度的所有滑块的集合,这个超长的‘胶片’用translate3d控制移动,使其某一格可以暴露在窗口中
移动的动画效果为 transition: 0.6s ease-in-out

循环体套用父元素模板

子组件(Carousel)中,为每个item套用其宿主定义的template,这里用到了NgContainer和TemplateRef,NgContainer使组件可以内嵌视图模板,内嵌的内容(ng-template)用ViewChild读取,此处声明为dataItemTemplateRef,使用NgTemplateOutlet指令将dataItemTemplateRef插入到ng-content位置,同时可以用ngTemplateOutletContext指令为内嵌视图附加一个上下文对象,显然就是为传递循环体的item打造的。
使用#id指定要插入到ng-container的template
官方示例:

1
2
3
4
5
6
7
8
9
10
<ng-container *ngTemplateOutlet="greet"></ng-container>
<hr>
<ng-container *ngTemplateOutlet="eng; context: myContext"></ng-container>
<hr>
<ng-container *ngTemplateOutlet="svk; context: myContext"></ng-container>
<hr>

<ng-template #greet><span>Hello</span></ng-template>
<ng-template #eng let-name><span>Hello {{name}}!</span></ng-template>
<ng-template #svk let-person="localSk"><span>Ahoj {{person}}!</span></ng-template>

其中 myContext = {$implicit: ‘World’, localSk: ‘Svet’};模板中用let-绑定上下文, $implicit(implicit 隐式的)指默认值,也就是例如let-keyXX=”parameterXX”的语法在缺省=”parameterXX”的表达下的值

响应式解决方案

npm install @angular/sdk
注意该package大版本跟随Angular版本
Angular CDK Layout

1
2
3
4
5
6
7
8
9
10
11
12
constructor(private breakpointObserver: BreakpointObserver) {
this.breakpointObserver.observe([Breakpoints.XSmall, Breakpoints.Small]).subscribe(result => {
if (result.matches) {
// max-width: 959px
this.windowWidth = { width: `${this.slickWidth}px` };
this.interval = 1;
} else {
this.windowWidth = { width: `${this.slickWidth * 3}px` };
this.interval = 3;
}
});
}