Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

myml/angular-animate

Repository files navigation

使用 Angular Animate

偶然中在百度图片看到一个有意思的动画效果,如下图所示

正好闲来无事,就想着实现个类似的动画,也好学习下 Angular 的动画系统

首先是分析这个动画的一些特点。

  1. 鼠标悬停显示工具栏
  2. 鼠标移出隐藏工具栏
  3. 动画方向跟随鼠标方向

12 很容易通过 css 的 hover 伪类 实现,3却没办法使用 css 实现,因为 css 无法获取鼠标坐标。看来只能使用 Javascript 来实现了。

动手的第一步当然是创建个 Demo 项目,加点 css 把架子搭好。 然后添加 Angular 指令,给每个框框都附加上鼠标事件监听。 hover.directive.ts

@HostListener("mouseover", ["$event"]) mouseover(e: MouseEvent){
 ...
}
@HostListener("mouseout", ["$event"]) mouseout(e: MouseEvent){
 ...
}

这里使用的HostListener在 Angular 中用于监听宿主元素事件,["$event"]则是将事件对象传递到函数内。 关于判断鼠标的方向,我这里用了一个比较简单的方法,通过找到距离鼠标最近的边来判定。

 // `x,y`分别是鼠标相对于元素的 X 坐标和 Y 坐标
 // `w,h`分别是元素的宽度和高度
 getDirection([x, y, w, h]: number[]) {
 return [
 { name: "top", value: y },
 { name: "bottom", value: h - y },
 { name: "left", value: x },
 { name: "right", value: w - x }
 ].sort((a, b) => a.value - b.value)[0].name;
 }

在事件回调中传递事件对象和元素对象的属性,即可得到事件方向。

const direction = this.getDirection([
 e.offsetX,
 e.offsetY,
 this.el.offsetWidth,
 this.el.offsetHeight
]);

前期准备工作都完成后,就要开始写动画了。

这里我用的是 Angular 的BrowserAnimationsModule模块来实现动画,所以要先在AppModule中导入BrowserAnimationsModule

import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
@NgModule({
 declarations: [AppComponent, HoverDirective],
 imports: [BrowserModule, BrowserAnimationsModule],
 providers: [],
 bootstrap: [AppComponent]
})

BrowserAnimationsModule中有两套动画系统:

  1. animations 基于CSS Animations,使用时附加于组件的元数据中,通过切换预定义的状态来实现动画过度
  2. AnimationBuilder。基于Web Animations API,使用时依赖AnimationBuilder服务,通过脚本代码动态创建动画并执行。

刚开始我是用animations实现了动画效果,却发现因为动画要跟随鼠标方向,使用animations会显得繁琐而效果也不甚满意,所以改用AnimationBuilder实现,代价是在老旧的浏览器上需要附带 polyfills 以支持Web Animations API,好在 Angular6 之后的版本会自动附带,无需任何配置。

先实现鼠标悬停动画

 @Input("appHover") backgroundImage: string;
 @Input("timings") timings: string | number = 200;
 get positions() {
 const [w, h] = [this.el.offsetWidth, this.el.offsetHeight];
 return {
 top: `0 -${h}px`,
 bottom: `0 ${h}px`,
 left: `-${w}px 0`,
 right: `${w}px 0`
 };
 }
 @HostListener("mouseover", ["$event"]) mouseover(e: MouseEvent) {
 const direction = this.getDirection([
 e.offsetX,
 e.offsetY,
 this.el.offsetWidth,
 this.el.offsetHeight
 ]);
 this.builder
 .build([
 style({
 backgroundImage: this.backgroundImage,
 backgroundRepeat: "no-repeat",
 backgroundSize: "100% 100%",
 backgroundPosition: this.positions[direction]
 }),
 animate(this.timings, style({ backgroundPosition: "0" }))
 ])
 .create(this.el)
 .play();
 }

backgroundImage,timings为输入属性,用于设置背景图片和动画时间。下面是使用例子

<div
 *ngFor="let i of arr; index as index"
 class="item"
 appHover="url(/assets/e1370ff509f36594499e5a6a98de3d6a.jpeg)"
 [timings]="'500ms ease-in-out'"
></div>

positions会根据元素宽高返回背景图初始位置,我所实现的动画过程就是在鼠标移入时,背景图会从鼠标移入方向展开。例如鼠标从元素上方移入,背景图从{x:0, y:-100%}移动到{x:0, y:0},因为backgroundPosition的局限性,在设置backgroundSize为百分比后,无法再使用百分比确定位置,只好使用元素的宽高像素确定位置。

builder.build就是整个动画的关键部分了,其实是很简单的三步调用

  1. 设置初始样式和动画样式
  2. 绑定到当前元素
  3. 执行动画

移出的动画就更好设置了,只需要将背景图移回初始位置即可。这个初始位置并不是固定的,而是根据移出方向变动的。

 @HostListener("mouseout", ["$event"]) mouseout(e: MouseEvent) {
 const direction = this.getDirection([
 e.offsetX,
 e.offsetY,
 this.el.offsetWidth,
 this.el.offsetHeight
 ]);
 this.builder
 .build([
 animate(
 this.timings,
 style({ backgroundPosition: this.positions[direction] })
 )
 ])
 .create(this.el)
 .play();
 }

这样整个动画就实现了,最终效果如下

Releases

No releases published

Packages

No packages published

AltStyle によって変換されたページ (->オリジナル) /