123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- import { Directive } from 'vue';
- import './index.less';
- export interface RippleOptions {
- event: string;
- transition: number;
- }
- export interface RippleProto {
- background?: string;
- zIndex?: string;
- }
- export type EventType = Event & MouseEvent & TouchEvent;
- const options: RippleOptions = {
- event: 'mousedown',
- transition: 400,
- };
- const RippleDirective: Directive & RippleProto = {
- beforeMount: (el: HTMLElement, binding) => {
- if (binding.value === false) return;
- const bg = el.getAttribute('ripple-background');
- setProps(Object.keys(binding.modifiers), options);
- const background = bg || RippleDirective.background;
- const zIndex = RippleDirective.zIndex;
- el.addEventListener(options.event, (event: EventType) => {
- rippler({
- event,
- el,
- background,
- zIndex,
- });
- });
- },
- updated(el, binding) {
- if (!binding.value) {
- el?.clearRipple?.();
- return;
- }
- const bg = el.getAttribute('ripple-background');
- el?.setBackground?.(bg);
- },
- };
- function rippler({
- event,
- el,
- zIndex,
- background,
- }: { event: EventType; el: HTMLElement } & RippleProto) {
- const targetBorder = parseInt(getComputedStyle(el).borderWidth.replace('px', ''));
- const clientX = event.clientX || event.touches[0].clientX;
- const clientY = event.clientY || event.touches[0].clientY;
- const rect = el.getBoundingClientRect();
- const { left, top } = rect;
- const { offsetWidth: width, offsetHeight: height } = el;
- const { transition } = options;
- const dx = clientX - left;
- const dy = clientY - top;
- const maxX = Math.max(dx, width - dx);
- const maxY = Math.max(dy, height - dy);
- const style = window.getComputedStyle(el);
- const radius = Math.sqrt(maxX * maxX + maxY * maxY);
- const border = targetBorder > 0 ? targetBorder : 0;
- const ripple = document.createElement('div');
- const rippleContainer = document.createElement('div');
- // Styles for ripple
- ripple.className = 'ripple';
- Object.assign(ripple.style ?? {}, {
- marginTop: '0px',
- marginLeft: '0px',
- width: '1px',
- height: '1px',
- transition: `all ${transition}ms cubic-bezier(0.4, 0, 0.2, 1)`,
- borderRadius: '50%',
- pointerEvents: 'none',
- position: 'relative',
- zIndex: zIndex ?? '9999',
- backgroundColor: background ?? 'rgba(0, 0, 0, 0.12)',
- });
- // Styles for rippleContainer
- rippleContainer.className = 'ripple-container';
- Object.assign(rippleContainer.style ?? {}, {
- position: 'absolute',
- left: `${0 - border}px`,
- top: `${0 - border}px`,
- height: '0',
- width: '0',
- pointerEvents: 'none',
- overflow: 'hidden',
- });
- const storedTargetPosition =
- el.style.position.length > 0 ? el.style.position : getComputedStyle(el).position;
- if (storedTargetPosition !== 'relative') {
- el.style.position = 'relative';
- }
- rippleContainer.appendChild(ripple);
- el.appendChild(rippleContainer);
- Object.assign(ripple.style, {
- marginTop: `${dy}px`,
- marginLeft: `${dx}px`,
- });
- const {
- borderTopLeftRadius,
- borderTopRightRadius,
- borderBottomLeftRadius,
- borderBottomRightRadius,
- } = style;
- Object.assign(rippleContainer.style, {
- width: `${width}px`,
- height: `${height}px`,
- direction: 'ltr',
- borderTopLeftRadius,
- borderTopRightRadius,
- borderBottomLeftRadius,
- borderBottomRightRadius,
- });
- setTimeout(() => {
- const wh = `${radius * 2}px`;
- Object.assign(ripple.style ?? {}, {
- width: wh,
- height: wh,
- marginLeft: `${dx - radius}px`,
- marginTop: `${dy - radius}px`,
- });
- }, 0);
- function clearRipple() {
- setTimeout(() => {
- ripple.style.backgroundColor = 'rgba(0, 0, 0, 0)';
- }, 250);
- setTimeout(() => {
- rippleContainer?.parentNode?.removeChild(rippleContainer);
- }, 850);
- el.removeEventListener('mouseup', clearRipple, false);
- el.removeEventListener('mouseleave', clearRipple, false);
- el.removeEventListener('dragstart', clearRipple, false);
- setTimeout(() => {
- let clearPosition = true;
- for (let i = 0; i < el.childNodes.length; i++) {
- if ((el.childNodes[i] as Recordable).className === 'ripple-container') {
- clearPosition = false;
- }
- }
- if (clearPosition) {
- el.style.position = storedTargetPosition !== 'static' ? storedTargetPosition : '';
- }
- }, options.transition + 260);
- }
- if (event.type === 'mousedown') {
- el.addEventListener('mouseup', clearRipple, false);
- el.addEventListener('mouseleave', clearRipple, false);
- el.addEventListener('dragstart', clearRipple, false);
- } else {
- clearRipple();
- }
- (el as Recordable).setBackground = (bgColor: string) => {
- if (!bgColor) {
- return;
- }
- ripple.style.backgroundColor = bgColor;
- };
- }
- function setProps(modifiers: Hash<any>, props: Recordable) {
- modifiers.forEach((item: Recordable) => {
- if (isNaN(Number(item))) props.event = item;
- else props.transition = item;
- });
- }
- export default RippleDirective;
|