refactor(button): split wave logic

This commit is contained in:
07akioni 2020-06-26 18:01:33 +08:00
parent 682eb5fdd2
commit 75b48c7c69
9 changed files with 223 additions and 156 deletions

2
src/_base/wave/index.js Normal file
View File

@ -0,0 +1,2 @@
import Wave from './src/Wave.vue'
export default Wave

View File

@ -0,0 +1,50 @@
<template>
<div
class="n-base-wave"
:class="{
'n-base-wave--active': active
}"
/>
</template>
<script>
import usecssr from '../../../_mixins/usecssr'
import styles from './styles/index.js'
export default {
name: 'NBaseWave',
mixins: [
usecssr(styles)
],
data () {
return {
active: false,
animationTimerId: null
}
},
beforeDestroy () {
const animationTimerId = this.animationTimerId
if (animationTimerId !== null) {
window.clearTimeout(animationTimerId)
}
},
methods: {
play () {
const animationTimerId = this.animationTimerId
if (animationTimerId !== null) {
window.clearTimeout(this.animationTimerId)
this.active = false
this.animationTimerId = null
}
this.$nextTick(() => {
void this.$el.offsetHeight
this.active = true
this.animationTimerId = window.setTimeout(() => {
this.active = false
this.animationTimerId = null
}, 1000)
})
}
}
}
</script>

View File

@ -0,0 +1,14 @@
import { cB, c } from '../../../../_utils/cssr/index.js'
export default c([
() => cB('base-wave', {
raw: `
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
border-radius: inherit;
`
})
])

View File

@ -0,0 +1,7 @@
import baseStyle from './base.cssr.js'
export default [
{
CNode: baseStyle
}
]

View File

@ -12,7 +12,6 @@
'n-button--disabled': disabled,
'n-button--loading': loading,
'n-button--block': block,
'n-button--rippling': rippling,
'n-button--enter-pressed': enterPressed,
'n-button--ghost': ghost,
'n-button--text': text,
@ -86,6 +85,7 @@
>
<slot />
</div>
<n-base-wave v-if="!text" ref="wave" />
<div class="n-button__border-mask" />
</button>
</template>
@ -98,6 +98,7 @@ import usecssr from '../../_mixins/usecssr'
import NFadeInHeightExpandTransition from '../../_transition/FadeInHeightExpandTransition'
import NIconSwitchTransition from '../../_transition/IconSwitchTransition'
import NBaseLoading from '../../_base/Loading'
import NBaseWave from '../../_base/wave'
import NIcon from '../../Icon'
import styles from './styles/index.js'
// import { read, hash, createHoverColor, createActiveColor } from '../../_utils/color'
@ -150,6 +151,7 @@ export default {
name: 'NButton',
components: {
NBaseLoading,
NBaseWave,
NIcon,
NIconSwitchTransition,
NFadeInHeightExpandTransition
@ -254,9 +256,7 @@ export default {
},
data () {
return {
enterPressed: false,
rippling: false,
rippleTimer: null
enterPressed: false
}
},
computed: {
@ -335,10 +335,6 @@ export default {
// if (colorHash) {
// unmountColorStyle(colorHash)
// }
const rippleTimer = this.rippleTimer
if (rippleTimer !== null) {
window.clearTimeout(rippleTimer)
}
},
methods: {
handleMouseDown (e) {
@ -354,17 +350,10 @@ export default {
if (!this.disabled) {
this.$emit('click', e)
if (!this.text) {
window.clearTimeout(this.rippleTimer)
this.rippleTimer = null
this.rippling = false
this.$nextTick().then(() => {
void this.$el.offsetHeight
this.rippling = true
this.rippleTimer = window.setTimeout(() => {
this.rippling = false
this.rippleTimer = null
}, 600)
})
const waveRef = this.$refs.wave
if (waveRef) {
waveRef.play()
}
}
}
},
@ -374,7 +363,7 @@ export default {
return
}
this.enterPressed = false
this.$nextTick().then(() => {
this.$nextTick(() => {
if (!this.disabled) {
this.$el.click()
}

View File

@ -1,20 +1,19 @@
import { c, cB, cTB2, cE, cM, cNotM, namespace } from '../../../_utils/cssr'
const ns = namespace
import { c, cB, cTB2, cE, cM, cNotM } from '../../../_utils/cssr'
function createRippleAnimation (digest, color, theme) {
return [
c(`@keyframes ${ns}-${theme ? theme + '-' : ''}button-${digest}-colored-ripple-spread`, {
c(`@keyframes ${theme}-${digest}-button-wave-spread`, {
from: {
boxShadow: `0 0 0 0 ${color}`
boxShadow: `0 0 0 ${color}`
},
to: {
boxShadow: `0 0 0 4px ${color}`
// don't use exact 5px since chrome will display the animation with glitches
boxShadow: `0 0 0.5px 4.5px ${color}`
}
}),
c(`@keyframes ${ns}-${theme ? theme + '-' : ''}button-${digest}-colored-ripple-opacity`, {
c(`@keyframes ${theme}-${digest}-button-wave-opacity`, {
from: {
opacity: 0.4
opacity: 0.6
},
to: {
opacity: 0
@ -59,6 +58,7 @@ export default c([
const digest = instanceColor || instanceType
const pallete = props.$local[digest]
const theme = props.$renderedTheme
const base = props.$base
return [
createRippleAnimation(
digest,
@ -68,162 +68,166 @@ export default c([
cTB2(
'button',
[
cM(`${digest}-colored`, [
cM(`${digest}-colored`, createColorProps(
pallete.textColor,
pallete.color,
pallete.borderColor || pallete.color
), [
createIconColorStyle(pallete.textColor),
digest === 'default' ? [
cM('tertiary-icon-depth', [
createIconColorStyle(pallete.tertiaryDepthIconColor)
cB('base-wave', {
top: '-1px',
right: '-1px',
bottom: '-1px',
left: '-1px',
animationIterationCount: 1,
animationDuration: props.$local.waveDuration,
animationTimingFunction: `${base.easeOutCubicBezier} ${base.easeOutCubicBezier}`
}),
cM(`${digest}-colored`, createColorProps(
pallete.textColor,
pallete.color,
pallete.borderColor || pallete.color
), [
// wave animation
cB('base-wave', [
cM('active', {
zIndex: 1,
animationName: `${theme}-${digest}-button-wave-spread, ${theme}-${digest}-button-wave-opacity`
})
]),
// button styles
createIconColorStyle(pallete.textColor),
digest === 'default' ? [
createIconColorStyle(pallete.iconColor)
] : [],
cNotM('disabled', [
cM(
'enter-pressed',
createColorProps(
pallete.activeTextColor || pallete.textColor,
pallete.activeColor,
pallete.activeBorderColor || pallete.activeColor
),
[
createBorderMaskStyle(pallete.activeBorderColor || pallete.activeColor),
createIconColorStyle(pallete.activeTextColor || pallete.textColor)
]
),
cNotM('enter-pressed', [
c('&:hover', createColorProps(
pallete.hoverTextColor || pallete.textColor,
pallete.hoverColor,
pallete.hoverBorderColor || pallete.hoverColor
), [
createBorderMaskStyle(pallete.hoverBorderColor || pallete.hoverColor),
createIconColorStyle(pallete.hoverTextColor || pallete.textColor)
]),
c('&:active', createColorProps(
pallete.activeTextColor || pallete.textColor,
pallete.activeColor,
pallete.activeBorderColor || pallete.activeColor
), [
createBorderMaskStyle(pallete.activeBorderColor || pallete.activeColor),
createIconColorStyle(pallete.activeTextColor || pallete.textColor)
])
]),
c('&:not(:active):focus', [
cNotM('enter-pressed', createColorProps(
pallete.focusTextColor || pallete.textColor,
pallete.focusColor,
pallete.focusBorderColor || pallete.focusColor
), [
createBorderMaskStyle(pallete.focusBorderColor || pallete.focusColor),
createIconColorStyle(pallete.focusTextColor || pallete.textColor)
])
])
]),
cM(`ghost`, createColorProps(
pallete.ghostTypedTextColor || pallete.color, 'transparent', pallete.borderColor || pallete.color
), [
createIconColorStyle(pallete.ghostTypedTextColor || pallete.color),
digest === 'default' ? [
createIconColorStyle(pallete.iconColor)
] : [],
cNotM('disabled', [
cM(
'enter-pressed',
createColorProps(
pallete.activeTextColor || pallete.textColor,
pallete.activeColor,
pallete.ghostTypedActiveTextColor || pallete.activeColor,
'transparent',
pallete.activeBorderColor || pallete.activeColor
),
[
createBorderMaskStyle(pallete.activeBorderColor || pallete.activeColor),
createIconColorStyle(pallete.activeTextColor || pallete.textColor)
createIconColorStyle(pallete.ghostTypedActiveTextColor || pallete.activeColor)
]
),
cNotM('enter-pressed', [
c('&:hover', createColorProps(
pallete.hoverTextColor || pallete.textColor,
pallete.hoverColor,
pallete.ghostTypedHoverTextColor || pallete.hoverColor,
'transparent',
pallete.hoverBorderColor || pallete.hoverColor
), [
createBorderMaskStyle(pallete.hoverBorderColor || pallete.hoverColor),
createIconColorStyle(pallete.hoverTextColor || pallete.textColor)
createIconColorStyle(pallete.ghostTypedHoverTextColor || pallete.hoverColor)
]),
c('&:active', createColorProps(
pallete.activeTextColor || pallete.textColor,
pallete.activeColor,
pallete.ghostTypedActiveTextColor || pallete.activeColor,
'transparent',
pallete.activeBorderColor || pallete.activeColor
), [
createBorderMaskStyle(pallete.activeBorderColor || pallete.activeColor),
createIconColorStyle(pallete.activeTextColor || pallete.textColor)
createIconColorStyle(pallete.ghostTypedActiveTextColor || pallete.activeColor)
])
]),
c('&:not(:active):focus', [
cNotM('enter-pressed', createColorProps(
pallete.focusTextColor || pallete.textColor,
pallete.focusColor,
pallete.ghostTypedHoverTextColor || pallete.hoverColor,
'transparent',
pallete.focusBorderColor || pallete.focusColor
), [
createBorderMaskStyle(pallete.focusBorderColor || pallete.focusColor),
createIconColorStyle(pallete.focusTextColor || pallete.textColor)
createIconColorStyle(pallete.ghostTypedHoverTextColor || pallete.hoverColor)
])
])
])
]),
cM('text', ({
color: pallete.textTypedTextColor || pallete.color
}), [
createIconColorStyle(pallete.textTypedTextColor || pallete.color),
digest === 'default' ? [
createIconColorStyle(pallete.iconColor)
] : [],
cNotM('disabled', [
cM(
'enter-pressed',
createColorProps(
pallete.textTypedActiveTextColor || pallete.activeColor,
null,
null
),
[
createIconColorStyle(pallete.textTypedActiveTextColor || pallete.activeColor)
]
),
cNotM('enter-pressed', [
c('&:hover', createColorProps(
pallete.textTypedHoverTextColor || pallete.hoverColor,
null,
null
), [
createIconColorStyle(pallete.textTypedHoverTextColor || pallete.hoverColor)
]),
c('&:active', createColorProps(
pallete.textTypedActiveTextColor || pallete.activeColor,
null,
null
), [
createIconColorStyle(pallete.textTypedActiveTextColor || pallete.activeColor)
])
]),
cM('rippling', [
c('&::after', {
animationName: `${ns}-${theme ? theme + '-' : ''}button-${digest}-colored-ripple-spread, ${ns}-${theme ? theme + '-' : ''}button-${digest}-colored-ripple-opacity`
})
])
]),
cM(`ghost`, createColorProps(
pallete.ghostTypedTextColor || pallete.color, 'transparent', pallete.borderColor || pallete.color
), [
createIconColorStyle(pallete.ghostTypedTextColor || pallete.color),
digest === 'default' ? [
cM('tertiary-icon-depth', [
createIconColorStyle(pallete.tertiaryDepthIconColor)
])
] : [],
cNotM('disabled', [
cM(
'enter-pressed',
createColorProps(
pallete.ghostTypedActiveTextColor || pallete.activeColor,
'transparent',
pallete.activeBorderColor || pallete.activeColor
),
[
createBorderMaskStyle(pallete.activeBorderColor || pallete.activeColor),
createIconColorStyle(pallete.ghostTypedActiveTextColor || pallete.activeColor)
]
),
cNotM('enter-pressed', [
c('&:hover', createColorProps(
pallete.ghostTypedHoverTextColor || pallete.hoverColor,
'transparent',
pallete.hoverBorderColor || pallete.hoverColor
), [
createBorderMaskStyle(pallete.hoverBorderColor || pallete.hoverColor),
createIconColorStyle(pallete.ghostTypedHoverTextColor || pallete.hoverColor)
]),
c('&:active', createColorProps(
pallete.ghostTypedActiveTextColor || pallete.activeColor,
'transparent',
pallete.activeBorderColor || pallete.activeColor
), [
createBorderMaskStyle(pallete.activeBorderColor || pallete.activeColor),
createIconColorStyle(pallete.ghostTypedActiveTextColor || pallete.activeColor)
])
]),
c('&:not(:active):focus', [
cNotM('enter-pressed', createColorProps(
pallete.ghostTypedHoverTextColor || pallete.hoverColor,
'transparent',
pallete.focusBorderColor || pallete.focusColor
), [
createBorderMaskStyle(pallete.focusBorderColor || pallete.focusColor),
createIconColorStyle(pallete.ghostTypedHoverTextColor || pallete.hoverColor)
])
])
])
]),
cM('text', ({
color: pallete.textTypedTextColor || pallete.color
}), [
createIconColorStyle(pallete.textTypedTextColor || pallete.color),
digest === 'default' ? [
cM('tertiary-icon-depth', [
createIconColorStyle(pallete.tertiaryDepthIconColor)
])
] : [],
cNotM('disabled', [
cM(
'enter-pressed',
createColorProps(
pallete.textTypedActiveTextColor || pallete.activeColor,
null,
null
),
[
createIconColorStyle(pallete.textTypedActiveTextColor || pallete.activeColor)
]
),
cNotM('enter-pressed', [
c('&:hover', createColorProps(
pallete.textTypedHoverTextColor || pallete.hoverColor,
null,
null
), [
createIconColorStyle(pallete.textTypedHoverTextColor || pallete.hoverColor)
]),
c('&:active', createColorProps(
pallete.textTypedActiveTextColor || pallete.activeColor,
null,
null
), [
createIconColorStyle(pallete.textTypedActiveTextColor || pallete.activeColor)
])
]),
c('&:not(:active):focus', [
cNotM('enter-pressed', createColorProps(
pallete.textTypedFocusTextColor || pallete.focusColor,
null,
null
), [
createIconColorStyle(pallete.textTypedFocusTextColor || pallete.focusColor)
])
c('&:not(:active):focus', [
cNotM('enter-pressed', createColorProps(
pallete.textTypedFocusTextColor || pallete.focusColor,
null,
null
), [
createIconColorStyle(pallete.textTypedFocusTextColor || pallete.focusColor)
])
])
])

View File

@ -28,5 +28,6 @@ export default {
small: '18px',
medium: '18px',
large: '20px'
}
},
waveDuration: '.6s'
}

View File

@ -35,7 +35,7 @@ export default create({
rippleColor: derived.primaryColor,
iconColor: derived.tertiaryTextColor
iconColor: derived.secondaryTextColor
},
primary: {
color: derived.primaryColor,

View File

@ -35,7 +35,7 @@ export default create({
rippleColor: derived.primaryColor,
iconColor: derived.tertiaryTextColor
iconColor: derived.secondaryTextColor
},
primary: {
color: derived.primaryColor,