[new feature] Transition: refactor with css transition

This commit is contained in:
rex 2019-02-25 16:01:21 +08:00 committed by GitHub
parent 74cb663c85
commit 11b6aa9b03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 280 additions and 248 deletions

View File

@ -3,7 +3,8 @@ import Page from '../../common/page';
Page({ Page({
data: { data: {
show: false, show: false,
name: 'fade' name: 'fade',
showCustom: false
}, },
onClickFade() { onClickFade() {
@ -47,5 +48,13 @@ Page({
setTimeout(() => { setTimeout(() => {
this.setData({ show: false }); this.setData({ show: false });
}, 500); }, 500);
},
onClickCustom() {
this.setData({ showCustom: true });
setTimeout(() => {
this.setData({ showCustom: false });
}, 500);
} }
}); });

View File

@ -8,10 +8,22 @@
<van-cell title="Slide Down" bind:click="onClickSlideDown" is-link /> <van-cell title="Slide Down" bind:click="onClickSlideDown" is-link />
<van-cell title="Slide Left" bind:click="onClickSlideLeft" is-link /> <van-cell title="Slide Left" bind:click="onClickSlideLeft" is-link />
<van-cell title="Slide Right" bind:click="onClickSlideRight" is-link /> <van-cell title="Slide Right" bind:click="onClickSlideRight" is-link />
<van-cell title="Custom" bind:click="onClickCustom" is-link />
<van-transition <van-transition
show="{{ show }}" show="{{ show }}"
name="{{ name }}" name="{{ name }}"
custom-class="block" custom-class="block"
/> />
<van-transition
show="{{ showCustom }}"
name=""
duration="{{ { enter: 300, leave: 1000 } }}"
custom-class="block"
enter-class="van-enter-class"
enter-active-class="van-enter-active-class"
leave-active-class="van-leave-active-class"
leave-to-class="van-leave-to-class"
/>
</demo-block> </demo-block>

View File

@ -1,9 +1,20 @@
.block { .block {
position: fixed;
top: 50%; top: 50%;
left: 50%; left: 50%;
width: 100px; width: 100px;
height: 100px; height: 100px;
position: fixed;
margin: -50px 0 0 -50px; margin: -50px 0 0 -50px;
background-color: #1989fa; background-color: #1989fa;
} }
.van-enter-active-class,
.van-leave-active-class {
transition-property: background-color, transform;
}
.van-enter-class,
.van-leave-to-class {
background-color: red;
transform: rotate(-360deg) translate3d(-100%, -100%, 0);
}

View File

@ -1,4 +1,15 @@
export const transition = function(showDefaultValue) { import { isObj } from '../common/utils';
const getClassNames = (name: string) => ({
enter: `van-${name}-enter van-${name}-enter-active enter-class enter-active-class`,
'enter-to': `van-${name}-enter-to van-${name}-enter-active enter-to-class enter-active-class`,
leave: `van-${name}-leave van-${name}-leave-active leave-class leave-active-class`,
'leave-to': `van-${name}-leave-to van-${name}-leave-active leave-to-class leave-active-class`
});
const requestAnimationFrame = (cb: Function) => setTimeout(cb, 1000 / 60);
export const transition = function(showDefaultValue: boolean) {
return Behavior({ return Behavior({
properties: { properties: {
customStyle: String, customStyle: String,
@ -8,8 +19,14 @@ export const transition = function(showDefaultValue) {
observer: 'observeShow' observer: 'observeShow'
}, },
duration: { duration: {
type: Number, type: [Number, Object],
value: 300 value: 300,
observer: 'observeDuration'
},
name: {
type: String,
value: 'fade',
observer: 'updateClasses'
} }
}, },
@ -17,53 +34,65 @@ export const transition = function(showDefaultValue) {
type: '', type: '',
inited: false, inited: false,
display: false, display: false,
supportAnimation: true classNames: getClassNames('fade')
}, },
attached() { attached() {
if (this.data.show) { if (this.data.show) {
this.show(); this.show();
} }
this.detectSupport();
}, },
methods: { methods: {
detectSupport() { observeShow(value: boolean) {
wx.getSystemInfo({
success: info => {
if (info && info.system && info.system.indexOf('iOS 8') === 0) {
this.set({ supportAnimation: false });
}
}
});
},
observeShow(value) {
if (value) { if (value) {
this.show(); this.show();
} else { } else {
if (this.data.supportAnimation) { this.leave();
this.set({ type: 'leave' });
} else {
this.set({ display: false });
}
} }
}, },
show() { updateClasses(name: string) {
this.set({ this.set({
inited: true, classNames: getClassNames(name)
display: true,
type: 'enter'
}); });
}, },
onAnimationEnd() { show() {
if (!this.data.show) { const { classNames, duration } = this.data;
this.set({
display: false this.set({
inited: true,
display: true,
classes: classNames.enter,
currentDuration: isObj(duration) ? duration.enter : duration
}).then(() => {
requestAnimationFrame(() => {
this.set({
classes: classNames['enter-to']
});
}); });
});
},
leave() {
const { classNames, duration } = this.data;
this.set({
classes: classNames.leave,
currentDuration: isObj(duration) ? duration.leave : duration
}).then(() => {
requestAnimationFrame(() => {
this.set({
classes: classNames['leave-to']
});
});
});
},
onTransitionEnd() {
if (!this.data.show) {
this.set({ display: false });
} }
} }
} }

View File

@ -1,7 +1,7 @@
.van-overlay { .van-overlay {
position: fixed; position: fixed;
top: 0; top: 0;
left: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
left: 0;
} }

View File

@ -6,6 +6,10 @@ VantComponent({
show: Boolean, show: Boolean,
mask: Boolean, mask: Boolean,
customStyle: String, customStyle: String,
duration: {
type: [Number, Object],
value: 300
},
zIndex: { zIndex: {
type: Number, type: Number,
value: 1 value: 1

View File

@ -2,6 +2,7 @@
show="{{ show }}" show="{{ show }}"
custom-class="van-overlay" custom-class="van-overlay"
custom-style="z-index: {{ zIndex }}; {{ mask ? 'background-color: rgba(0, 0, 0, .7);' : '' }}; {{ customStyle }}" custom-style="z-index: {{ zIndex }}; {{ mask ? 'background-color: rgba(0, 0, 0, .7);' : '' }}; {{ customStyle }}"
duration="{{ duration }}"
bind:tap="onClick" bind:tap="onClick"
catch:touchmove="noop" catch:touchmove="noop"
/> />

View File

@ -50,7 +50,7 @@ Page({
| z-index | z-index 层级 | `Number` | `100` | | z-index | z-index 层级 | `Number` | `100` |
| overlay | 是否显示背景蒙层 | `Boolean` | `true` | | overlay | 是否显示背景蒙层 | `Boolean` | `true` |
| position | 可选值为 `top` `bottom` `right` `left` | `String` | - | | position | 可选值为 `top` `bottom` `right` `left` | `String` | - |
| duration | 动画时长,单位为毫秒 | `Number` | `300` | | duration | 动画时长,单位为毫秒 | `Number | Object` | `300` |
| custom-style | 自定义弹出层样式 | `String` | `` | | custom-style | 自定义弹出层样式 | `String` | `` |
| overlay-style | 自定义背景蒙层样式 | `String` | `` | | overlay-style | 自定义背景蒙层样式 | `String` | `` |
| close-on-click-overlay | 点击蒙层是否关闭 Popup | `Boolean` | `true` | | close-on-click-overlay | 点击蒙层是否关闭 Popup | `Boolean` | `true` |
@ -68,12 +68,3 @@ Page({
| 类名 | 说明 | | 类名 | 说明 |
|-----------|-----------| |-----------|-----------|
| custom-class | 根节点样式类 | | custom-class | 根节点样式类 |
### 更新日志
| 版本 | 类型 | 内容 |
|-----------|-----------|-----------|
| 0.0.1 | feature | 新增组件 |
| 0.3.2 | feature | 支持退场动画 |
| 0.3.2 | feature | 新增 z-index 属性 |
| 0.3.3 | feature | 新增 custom-style 属性 |

View File

@ -1,26 +1,28 @@
@import '../common/style/var.less'; @import '../common/style/var.less';
.van-popup { .van-popup {
position: fixed;
top: 50%; top: 50%;
left: 50%; left: 50%;
position: fixed;
max-height: 100%; max-height: 100%;
overflow-y: auto; overflow-y: auto;
box-sizing: border-box;
background-color: @white; background-color: @white;
-webkit-overflow-scrolling: touch; box-sizing: border-box;
animation: ease both; animation: ease both;
-webkit-overflow-scrolling: touch;
transition-timing-function: ease;
&--center { &--center {
transform: translate3d(-50%, -50%, 0); transform: translate3d(-50%, -50%, 0);
} }
&--top { &--top {
width: 100%;
top: 0; top: 0;
right: auto; right: auto;
bottom: auto; bottom: auto;
left: 50%; left: 50%;
width: 100%;
transform: translate3d(-50%, 0, 0);
} }
&--right { &--right {
@ -28,14 +30,16 @@
right: 0; right: 0;
bottom: auto; bottom: auto;
left: auto; left: auto;
transform: translate3d(0, -50%, 0);
} }
&--bottom { &--bottom {
width: 100%;
top: auto; top: auto;
bottom: 0;
right: auto; right: auto;
bottom: 0;
left: 50%; left: 50%;
width: 100%;
transform: translate3d(-50%, 0, 0);
} }
&--left { &--left {
@ -43,6 +47,7 @@
right: auto; right: auto;
bottom: auto; bottom: auto;
left: 0; left: 0;
transform: translate3d(0, -50%, 0);
} }
&--safe { &--safe {
@ -50,100 +55,64 @@
} }
} }
@keyframes van-center-enter { .van-scale-enter-active,
from { .van-scale-leave-active {
opacity: 0; transition-property: opacity, transform;
}
} }
@keyframes van-center-leave { .van-scale-enter,
to { .van-scale-leave-to {
opacity: 0; opacity: 0;
} transform: translate3d(-50%, -50%, 0) scale(0.7);
} }
@keyframes van-scale-enter { .van-fade-enter-active,
from { .van-fade-leave-active {
opacity: 0; transition-property: opacity;
transform: translate3d(-50%, -50%, 0) scale(0.7);
}
} }
@keyframes van-scale-leave { .van-fade-enter,
to { .van-fade-leave-to {
opacity: 0; opacity: 0;
transform: translate3d(-50%, -50%, 0) scale(0.7);
}
} }
@keyframes van-bottom-enter { .van-center-enter-active,
from { .van-center-leave-active {
transform: translate3d(-50%, 100%, 0); transition-property: opacity;
}
to {
transform: translate3d(-50%, 0, 0);
}
} }
@keyframes van-bottom-leave { .van-center-enter,
from { .van-center-leave-to {
transform: translate3d(-50%, 0, 0); opacity: 0;
}
to {
transform: translate3d(-50%, 100%, 0);
}
} }
@keyframes van-top-enter { .van-bottom-enter-active,
from { .van-bottom-leave-active,
transform: translate3d(-50%, -100%, 0); .van-top-enter-active,
} .van-top-leave-active,
to { .van-left-enter-active,
transform: translate3d(-50%, 0, 0); .van-left-leave-active,
} .van-right-enter-active,
.van-right-leave-active {
transition-property: transform;
} }
@keyframes van-top-leave { .van-bottom-enter,
from { .van-bottom-leave-to {
transform: translate3d(-50%, 0, 0); transform: translate3d(-50%, 100%, 0);
}
to {
transform: translate3d(-50%, -100%, 0);
}
} }
@keyframes van-left-enter { .van-top-enter,
from { .van-top-leave-to {
transform: translate3d(-100%, -50%, 0); transform: translate3d(-50%, -100%, 0);
}
to {
transform: translate3d(0, -50%, 0);
}
} }
@keyframes van-left-leave { .van-left-enter,
from { .van-left-leave-to {
transform: translate3d(0, -50%, 0); transform: translate3d(-100%, -50%, 0);
}
to {
transform: translate3d(-100%, -50%, 0);
}
} }
@keyframes van-right-enter { .van-right-enter,
from { .van-right-leave-to {
transform: translate3d(100%, -50%, 0); transform: translate3d(100%, -50%, 0);
}
to {
transform: translate3d(0, -50%, 0);
}
}
@keyframes van-right-leave {
from {
transform: translate3d(0, -50%, 0);
}
to {
transform: translate3d(100%, -50%, 0);
}
} }

View File

@ -3,10 +3,22 @@ import { transition } from '../mixins/transition';
import { iphonex } from '../mixins/iphonex'; import { iphonex } from '../mixins/iphonex';
VantComponent({ VantComponent({
classes: [
'enter-class',
'enter-active-class',
'enter-to-class',
'leave-class',
'leave-active-class',
'leave-to-class'
],
mixins: [transition(false), iphonex], mixins: [transition(false), iphonex],
props: { props: {
transition: String, transition: {
type: String,
observer: 'observeClass'
},
customStyle: String, customStyle: String,
overlayStyle: String, overlayStyle: String,
zIndex: { zIndex: {
@ -23,10 +35,15 @@ VantComponent({
}, },
position: { position: {
type: String, type: String,
value: 'center' value: 'center',
observer: 'observeClass'
} }
}, },
created() {
this.observeClass();
},
methods: { methods: {
onClickOverlay() { onClickOverlay() {
this.$emit('click-overlay'); this.$emit('click-overlay');
@ -34,6 +51,11 @@ VantComponent({
if (this.data.closeOnClickOverlay) { if (this.data.closeOnClickOverlay) {
this.$emit('close'); this.$emit('close');
} }
},
observeClass() {
const { transition, position } = this.data;
this.updateClasses(transition || position);
} }
} }
}); });

View File

@ -6,13 +6,14 @@
show="{{ show }}" show="{{ show }}"
z-index="{{ zIndex }}" z-index="{{ zIndex }}"
custom-style="{{ overlayStyle }}" custom-style="{{ overlayStyle }}"
duration="{{ duration }}"
bind:click="onClickOverlay" bind:click="onClickOverlay"
/> />
<view <view
wx:if="{{ inited }}" wx:if="{{ inited }}"
class="custom-class {{ utils.bem('popup', [position, { safe: isIPhoneX && safeAreaInsetBottom && position === 'bottom' }]) }}" class="custom-class {{ classes }} {{ utils.bem('popup', [position, { safe: isIPhoneX && safeAreaInsetBottom && position === 'bottom' }]) }}"
style="z-index: {{ zIndex }}; -webkit-animation: van-{{ transition || position }}-{{ type }} {{ duration }}ms both; animation: van-{{ transition || position }}-{{ type }} {{ duration }}ms both; {{ display ? '' : 'display: none;' }}{{ customStyle }}" style="z-index: {{ zIndex }}; -webkit-transition-duration:{{ currentDuration }}ms; transition-duration:{{ currentDuration }}ms; {{ display ? '' : 'display: none;' }} {{ customStyle }}"
bind:animationend="onAnimationEnd" bind:transitionend="onTransitionEnd"
> >
<slot /> <slot />
</view> </view>

View File

@ -27,13 +27,42 @@ transition 组件内置了多种动画,可以通过`name`字段指定动画类
<van-transition name="fade-up" /> <van-transition name="fade-up" />
``` ```
#### 高级用法
可以通过外部样式类自定义过渡效果,还可以定制进入和移出的持续时间:
```html
<van-transition
show="{{ show }}"
name=""
duration="{{ { enter: 300, leave: 1000 } }}"
enter-class="van-enter-class"
enter-active-class="van-enter-active-class"
leave-active-class="van-leave-active-class"
leave-to-class="van-leave-to-class"
/>
```
```css
.van-enter-active-class,
.van-leave-active-class {
transition-property: background-color, transform;
}
.van-enter-class,
.van-leave-to-class {
background-color: red;
transform: rotate(-360deg) translate3d(-100%, -100%, 0);
}
```
### API ### API
| 参数 | 说明 | 类型 | 默认值 | | 参数 | 说明 | 类型 | 默认值 |
|-----------|-----------|-----------|-------------| |-----------|-----------|-----------|-------------|
| name | 动画类型 | `String` | `fade`| | name | 动画类型 | `String` | `fade`|
| show | 是否展示组件 | `Boolean` | `true` | | show | 是否展示组件 | `Boolean` | `true` |
| duration | 动画时长,单位为毫秒 | `Number` | `300` | | duration | 动画时长,单位为毫秒 | `Number | Object` | `300` |
| custom-style | 自定义样式 | `String` | - | | custom-style | 自定义样式 | `String` | - |
### 外部样式类 ### 外部样式类
@ -41,6 +70,12 @@ transition 组件内置了多种动画,可以通过`name`字段指定动画类
| 类名 | 说明 | | 类名 | 说明 |
|-----------|-----------| |-----------|-----------|
| custom-class | 根节点样式类 | | custom-class | 根节点样式类 |
| enter-class | 定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。|
| enter-active-class | 定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。 |
| enter-to-class | 定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 enter-class 被移除),在过渡/动画完成之后移除。 |
| leave-class | 定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。|
| leave-active-class | 定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。 |
| leave-to-class | 定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 leave-class 被删除),在过渡/动画完成之后移除。 |
### 动画类型 ### 动画类型
@ -55,9 +90,3 @@ transition 组件内置了多种动画,可以通过`name`字段指定动画类
| slide-down | 下滑进入 | | slide-down | 下滑进入 |
| slide-left | 左滑进入 | | slide-left | 左滑进入 |
| slide-right | 右滑进入 | | slide-right | 右滑进入 |
### 更新日志
| 版本 | 类型 | 内容 |
|-----------|-----------|-----------|
| 0.1.1 | feature | 新增组件 |

View File

@ -1,127 +1,79 @@
.van-transition { .van-transition {
animation: ease both; transition-timing-function: ease;
} }
@keyframes van-fade-enter { .van-fade-enter-active,
from { .van-fade-leave-active {
opacity: 0; transition-property: opacity;
}
to {
opacity: 1;
}
} }
@keyframes van-fade-leave { .van-fade-enter,
from { .van-fade-leave-to {
opacity: 1; opacity: 0;
}
to {
opacity: 0;
}
} }
@keyframes van-fade-up-enter { .van-fade-up-enter-active,
from { .van-fade-up-leave-active,
opacity: 0; .van-fade-down-enter-active,
transform: translate3d(0, 100%, 0); .van-fade-down-leave-active,
} .van-fade-left-enter-active,
.van-fade-left-leave-active,
.van-fade-right-enter-active,
.van-fade-right-leave-active {
transition-property: opacity, transform;
} }
@keyframes van-fade-up-leave { .van-fade-up-enter,
to { .van-fade-up-leave-to {
opacity: 0; opacity: 0;
transform: translate3d(0, 100%, 0); transform: translate3d(0, 100%, 0);
}
} }
@keyframes van-slide-up-enter { .van-fade-down-enter,
from { .van-fade-down-leave-to {
transform: translate3d(0, 100%, 0); opacity: 0;
} transform: translate3d(0, -100%, 0);
} }
@keyframes van-slide-up-leave { .van-fade-left-enter,
to { .van-fade-left-leave-to {
transform: translate3d(0, 100%, 0); opacity: 0;
} transform: translate3d(-100%, 0, 0);
} }
@keyframes van-fade-down-enter { .van-fade-right-enter,
from { .van-fade-right-leave-to {
opacity: 0; opacity: 0;
transform: translate3d(0, -100%, 0); transform: translate3d(100%, 0, 0);
}
} }
@keyframes van-fade-down-leave { .van-slide-up-enter-active,
to { .van-slide-up-leave-active,
opacity: 0; .van-slide-down-enter-active,
transform: translate3d(0, -100%, 0); .van-slide-down-leave-active,
} .van-slide-left-enter-active,
.van-slide-left-leave-active,
.van-slide-right-enter-active,
.van-slide-right-leave-active {
transition-property: transform;
} }
@keyframes van-slide-down-enter { .van-slide-up-enter,
from { .van-slide-up-leave-to {
transform: translate3d(0, -100%, 0); transform: translate3d(0, 100%, 0);
}
} }
@keyframes van-slide-down-leave { .van-slide-down-enter,
to { .van-slide-down-leave-to {
transform: translate3d(0, -100%, 0); transform: translate3d(0, -100%, 0);
}
} }
@keyframes van-fade-left-enter { .van-slide-left-enter,
from { .van-slide-left-leave-to {
opacity: 0; transform: translate3d(-100%, 0, 0);
transform: translate3d(-100%, 0, 0);
}
} }
@keyframes van-fade-left-leave { .van-slide-right-enter,
to { .van-slide-right-leave-to {
opacity: 0; transform: translate3d(100%, 0, 0);
transform: translate3d(-100%, 0, 0);
}
}
@keyframes van-slide-left-enter {
from {
transform: translate3d(-100%, 0, 0);
}
}
@keyframes van-slide-left-leave {
to {
transform: translate3d(-100%, 0, 0);
}
}
@keyframes van-fade-right-enter {
from {
opacity: 0;
transform: translate3d(100%, 0, 0);
}
}
@keyframes van-fade-right-leave {
to {
opacity: 0;
transform: translate3d(100%, 0, 0);
}
}
@keyframes van-slide-right-enter {
from {
transform: translate3d(100%, 0, 0);
}
}
@keyframes van-slide-right-leave {
to {
transform: translate3d(100%, 0, 0);
}
} }

View File

@ -2,12 +2,14 @@ import { VantComponent } from '../common/component';
import { transition } from '../mixins/transition'; import { transition } from '../mixins/transition';
VantComponent({ VantComponent({
mixins: [transition(true)], classes: [
'enter-class',
'enter-active-class',
'enter-to-class',
'leave-class',
'leave-active-class',
'leave-to-class'
],
props: { mixins: [transition(true)]
name: {
type: String,
value: 'fade'
}
}
}); });

View File

@ -1,8 +1,8 @@
<view <view
wx:if="{{ inited }}" wx:if="{{ inited }}"
class="van-transition custom-class" class="van-transition custom-class {{ classes }}"
style="-webkit-animation: van-{{ name }}-{{ type }} {{ duration }}ms both; animation: van-{{ name }}-{{ type }} {{ duration }}ms both; {{ display ? '' : 'display: none;' }} {{ customStyle }}" style="-webkit-transition-duration:{{ currentDuration }}ms; transition-duration:{{ currentDuration }}ms; {{ display ? '' : 'display: none;' }} {{ customStyle }}"
bind:animationend="onAnimationEnd" bind:transitionend="onTransitionEnd"
> >
<slot /> <slot />
</view> </view>