解决transitionend会多次触发问题

399 阅读1分钟

导致执行多次原因是因为transition过渡过程中发生不止一个元素改动,

比如说同时过渡了opacity,transform就会执行两次

const TRANSITION = "transition";
const ANIMATION = "animation"
function getTimeout(delays: string[], durations: string[]): number {
  while (delays.length < durations.length) {
    delays = delays.concat(delays)
  }
  return Math.max(...durations.map((d, i) => toMs(d) + toMs(delays[i])))
}
function toMs(s: string): number {
    return Number(s.slice(0, -1).replace(',', '.')) * 1000
}

type TransitionType = typeof ANIMATION | typeof TRANSITION;

export const getTransitionInfo = (el: HTMLElement, type : TransitionType) => {
    const styles: any = window.getComputedStyle(el);
    const getStyleProperties = (key: string) => (styles[key] || '').split(', ');
    const transitionDelays = getStyleProperties(TRANSITION + 'Delay')
    const transitionDurations = getStyleProperties(TRANSITION + 'Duration')
    const transitionTimeout = getTimeout(transitionDelays, transitionDurations)
    const animationDelays = getStyleProperties(ANIMATION + 'Delay')
    const animationDurations = getStyleProperties(ANIMATION + 'Duration')
    const animationTimeout = getTimeout(animationDelays, animationDurations)
    let timeout = 0
    let propCount = 0
    if (type === 'transition' && transitionTimeout > 0) {
        timeout = transitionTimeout;
        propCount = transitionDurations.length;
    } else if (type === 'animation' && animationTimeout > 0) {
        timeout = animationTimeout;
        propCount = animationDurations.length;
    }

  const hasTransform =
    type === TRANSITION &&
    /\b(transform|all)(,|$)/.test(styles[TRANSITION + 'Property'])
    return {
        type,
        timeout,
        propCount,
        hasTransform
    }
}

export const listenerTransitionEnd = (el: HTMLElement, type : TransitionType, callback: Function) => {

  const { timeout, propCount } = getTransitionInfo(el, type)
  const endEvent = type + 'end';
  let ended = 0
  const end = () => {
    el.removeEventListener(endEvent, onEnd)
    callback && callback();
  }
  if (propCount === 0) {
    callback && callback();
    return { timer: -1, timeout, propCount, type }
  }
  const onEnd = (e: Event) => {
    if (e.target === el && ++ended >= propCount) {
      end()
    }
  }
  const timer = setTimeout(() => {
    if (ended < propCount) {
      end()
    }
  }, timeout + 1)
  el.addEventListener(endEvent, onEnd)
  return {
    timer, timeout, propCount, type
  }
}

使用方式



listenerTransitionEnd($el.current, props.type || 'transition', () => {
    console.log('动画执行完成')
})