UNPKG

press-next

Version:

Vue3 组件库,支持 Composition API

155 lines (130 loc) 4.76 kB
import { ref, onMounted, onUnmounted } from 'vue'; export interface Styles { [css: string]: string | number; } /** * 用于为节点增加styles * @param el HTMLElement * @param style Styles */ function setStyle(el: HTMLElement, styles: Styles): void { const keys = Object.keys(styles); keys.forEach((key) => { // @ts-ignore // eslint-disable-next-line no-param-reassign el.style[key] = styles[key]; }); } const period = 200; const noneRippleBg = 'rgba(0, 0, 0, 0)'; const defaultRippleColor = 'rgba(0, 0, 0, 0.35)'; // 设置动画颜色 get the ripple animation color const getRippleColor = (el: HTMLElement, fixedRippleColor?: string) => { // get fixed color from params if (fixedRippleColor) { return fixedRippleColor; } // get dynamic color from the dataset if (el?.dataset?.ripple) { const rippleColor = el.dataset.ripple; return rippleColor; } // use css variable const cssVariable = getComputedStyle(el).getPropertyValue('--ripple-color'); if (cssVariable) { return cssVariable; } return defaultRippleColor; }; /** * 斜八角动画hooks 支持三种方式使用 * 1. fixedRippleColor 固定色值 useRipple(ref,fixedRippleColor); * 2. dynamicColor 动态色值 data.ripple="rippleColor" useRipple(ref) * 3. CSS variables(recommended) 配合节点对应CSS设置 --ripple-color useRipple(ref) * @param ref 需要使用斜八角动画的DOM * @param fixedRippleColor 斜八角的动画颜色 */ export function useRipple(el: any, fixedRippleColor?: any) { const rippleContainer: any = ref(null); // 为节点添加斜八角动画 add ripple to the DOM and set up the animation const handleAddRipple = (e: MouseEvent) => { const dom = el.value; const rippleColor = getRippleColor(dom, fixedRippleColor?.value); if (e.button !== 0 || !el) return; const elStyle = getComputedStyle(dom); const elBorder = parseInt(elStyle.borderWidth, 10); const border = elBorder > 0 ? elBorder : 0; const width = dom.offsetWidth; const height = dom.offsetHeight; if (rippleContainer.value.parentNode === null) { setStyle(rippleContainer.value, { position: 'absolute', left: `${0 - border}px`, top: `${0 - border}px`, width: `${width}px`, height: `${height}px`, borderRadius: elStyle.borderRadius, pointerEvents: 'none', overflow: 'hidden', }); dom.appendChild(rippleContainer.value); } // 新增一个ripple const ripple = document.createElement('div'); setStyle(ripple, { marginTop: '0', marginLeft: '0', right: `${width}px`, width: `${width + 20}px`, height: '100%', transition: `transform ${period}ms cubic-bezier(.38, 0, .24, 1), background ${period * 2}ms linear`, transform: 'skewX(-8deg)', pointerEvents: 'none', position: 'absolute', zIndex: 0, backgroundColor: rippleColor, opacity: '0.9', }); // fix zIndex:避免遮盖内部元素 const elMap = new WeakMap(); for (let n = dom.children.length, i = 0; i < n; ++i) { const child = dom.children[i]; if ((child as HTMLElement).style.zIndex === '' && child !== rippleContainer.value) { (child as HTMLElement).style.zIndex = '1'; elMap.set(child, true); } } // fix position const initPosition = dom.style.position ? dom.style.position : getComputedStyle(dom).position; if (initPosition === '' || initPosition === 'static') { // eslint-disable-next-line no-param-reassign dom.style.position = 'relative'; } rippleContainer.value.insertBefore(ripple, rippleContainer.value.firstChild); setTimeout(() => { ripple.style.transform = `translateX(${width}px)`; }, 0); // 清除动画节点 clear ripple container const handleClearRipple = () => { ripple.style.backgroundColor = noneRippleBg; if (!el.value) return; el.value.removeEventListener('pointerup', handleClearRipple, false); el.value.removeEventListener('pointerleave', handleClearRipple, false); setTimeout(() => { ripple.remove(); if (rippleContainer.value.children.length === 0) rippleContainer.value.remove(); }, period * 2 + 100); }; el.value.addEventListener('pointerup', handleClearRipple, false); el.value.addEventListener('pointerleave', handleClearRipple, false); }; onMounted(() => { const dom = el?.value; if (!dom) return; rippleContainer.value = document.createElement('div'); dom.addEventListener('pointerdown', handleAddRipple, false); }); onUnmounted(() => { el?.value?.removeEventListener('pointerdown', handleAddRipple, false); }); }