首页 > 其他分享 >骨架屏生成辅助函数

骨架屏生成辅助函数

时间:2022-12-26 11:14:18浏览次数:26  
标签:MAP style 辅助 函数 classList 骨架 return const string

/**
 * @description: A simple function to help you generate skeleton
 * Demo:
 *  const s = new Skeleton();
    const rootNode = document.querySelector('#root') as HTMLElement;
    const classList = [  // give className in which you wanna generate
      'module-icon',
      'module-title',
      'module-input',
      'module-btn-others-title-content',
      'module-btn-others-btn',
      'module-btn',
    ];
    const { html, style } = s.traverse({
      rootNode,
      classList,
      animation: "wave"
    }); 
    s.appendTo(html, style);
 * 
 */

const ANIMATION_TYPE_MAP = {
  WAVE: "wave",
  NONE: "none"
} as const;

type AnimationValueType = typeof ANIMATION_TYPE_MAP[keyof typeof ANIMATION_TYPE_MAP];

interface SkeletonOptionsType {
  rootNode: HTMLElement,

  classList: Array<string>,

  animation?: AnimationValueType,  // default is wave

  isFilterOutsideElements?: boolean;   // Whether to filter elements outside the screen, default is true

  borderRadiusFit?: "auto" | "fit-content";  // auto means if border-radius is less than width/2, use minBorderRadius, default is auto

  minBorderRadius?: number;  // min border radius, default is 3 
}

interface StyleType {
  position: "absolute",
  left: string;
  top: string;
  width: string;
  height: string;
  background: string;
  borderRadius: string;
  overflow: "hidden"
}

const DIRECTION = {
  HORIZONTAL: "HORIZONTAL",
  VERTICAL: "VERTICAL",
} as const;

type DirectionType = keyof typeof DIRECTION;


const KEYFRAMES_MAP = {
  WAVE: `@keyframes kf-wave {
    0% {
      transform: translateX(-100%);
    }
  
    50% {
      transform: translateX(100%);
    }
  
    100% {
      transform: translateX(100%);
    }
  }`
};

const ANIMATION_STYLE_MAP = {
  WAVE: `animation: kf-wave 1.6s linear 0.5s infinite;
  background: linear-gradient( 90deg, transparent, rgba(0, 0, 0, 0.04), transparent );
  content: '';
  position: absolute;
  transform: translateX(-100%);
  bottom: 0;
  left: 0;
  right: 0;
  top: 0;`
}

const BORDER_RADIUS_MAP = {
  AUTO: "auto",
  FIT_CONTENT: "fit-content",
}


class Skeleton {
  traverse(skeletonOptions: SkeletonOptionsType) {
    const { 
      rootNode, 
      classList, animation = ANIMATION_TYPE_MAP.WAVE, 
      borderRadiusFit=BORDER_RADIUS_MAP.AUTO, 
      minBorderRadius = 3,
      isFilterOutsideElements=true
    } =  skeletonOptions;
    const targetClassMap = this.generateClassMap(classList);

    const backTrack = (node) => {
      const children = node.children ?? [];
      const htmlList: Array<string> = [];
      let styleList: Array<string>= [];
      for (let child of children) {
        const { x, y, width, height, } = child.getBoundingClientRect();
        let style: StyleType | {} = {};
        if (this.isClassIntersect(child.classList, targetClassMap)) {
          if(isFilterOutsideElements && !this.isElementInView(x, y)) {
            continue;
          }
          style = {
            position: "absolute",
            left: this.transformToPercent(x, DIRECTION.HORIZONTAL),
            top: this.transformToPercent(y, DIRECTION.VERTICAL),
            width: this.transformToPercent(width, DIRECTION.HORIZONTAL),
            height: this.transformToPercent(height, DIRECTION.VERTICAL),
            background: "#F1F1F1",
            borderRadius: this.getBorderRadius(window.getComputedStyle(child)["border-radius"], borderRadiusFit, minBorderRadius, width),
            overflow: "hidden",
          }

          const className = child.classList[0] + "-skeleton";
          style = this.generateStyle(style);
          styleList.push(`.${className} {${style}}`);

          if(ANIMATION_TYPE_MAP.NONE !== animation) {
            styleList.push(`.${className}::after {${ANIMATION_STYLE_MAP[animation.toUpperCase()]}}`)
          }

          htmlList.push(`<div class=${className}>`);
          const childTarget = backTrack(child);
          htmlList.push(childTarget.htmlList.join(""));
          styleList.push(childTarget.styleList.join(""));
          htmlList.push("</div>");
          
        } else {
          const childTarget = backTrack(child);
          htmlList.push(childTarget.htmlList.join(""));
          styleList.push(childTarget.styleList.join(""));
        }
      }
      return {
        htmlList,
        styleList,
      };
    }

    const { htmlList, styleList } = backTrack(rootNode);
    let animationStyle = "";
    if(animation === ANIMATION_TYPE_MAP.WAVE) {
      animationStyle = `${KEYFRAMES_MAP.WAVE}`;
    }
    return {
      html: `<div className="i-skeleton">${htmlList.join("")}</div>`,
      style: animationStyle + styleList.join("")
    }
  }

  appendTo (html: string, style: string) :void {
    console.log(html);
    console.log(style)
    const styleElement = document.createElement("style");
    styleElement.innerHTML = style;
    document.head.appendChild(styleElement);
    document.body.innerHTML = html;
  }

  getBorderRadius (currentBorderRadius: string | number, borderRadiusMod: string, minBorderRadius: number, width: number) :string {
    currentBorderRadius = Number.parseInt(currentBorderRadius as string);
    let borderRadius = minBorderRadius;
    switch (borderRadiusMod) {
      case BORDER_RADIUS_MAP.AUTO:
        if(currentBorderRadius < width / 2 - 1) {
          borderRadius = minBorderRadius;
        }
        break;
      case BORDER_RADIUS_MAP.FIT_CONTENT:
        borderRadius = currentBorderRadius;
        break;
      default:
        break;
    }
    return `${borderRadius}px`
  }

  generateClassMap(classList: Array<string>) :Map<string, boolean> {
    const map = new Map();
    for (let item of classList) {
      map.set(item, true);
    }
    return map;
  }

  isClassIntersect(classList: Array<string>, targetMap: Map<string, boolean>) {
    for (let item of classList) {
      if (targetMap.has(item)) {
        return true;
      }
    }
    return false;
  }

  isElementInView (x: number, y: number) :boolean {
    if(x > 0 && 
      x < document.documentElement.clientWidth && 
      y > 0 && 
      y < document.documentElement.clientHeight) {
      return true;
    }

    return false;
  }

  generateStyle(style): string {
    let result = "";
    for (let [key, value] of Object.entries(style)) {
      const keyWithMiddleLine = this.camelToMiddleLine(key);
      result += `${keyWithMiddleLine}: ${value};`;
    }
    return result;
  }

  camelToMiddleLine(str: string): string {
    const p = /\B([A-Z])/g;
    return str.replace(p, '-$1').toLowerCase()
  }

  transformToPercent(distance: number, direction: DirectionType): string {
    const viewportWidth = document.documentElement.clientWidth;
    const viewportHeight = document.documentElement.clientHeight;
    switch (direction) {
      case DIRECTION.HORIZONTAL:
        return (distance / viewportWidth * 100).toFixed(1) + "%"
        break;
      default:
        return (distance / viewportHeight * 100).toFixed(1) + "%"
    }
  }
}

export default Skeleton;

 

标签:MAP,style,辅助,函数,classList,骨架,return,const,string
From: https://www.cnblogs.com/cheng-up/p/17005251.html

相关文章

  • STL string 常用函数
    string类的构造函数:string(constchar*s);//用c字符串s初始化string(intn,charc);//用n个字符c初始化此外,string类还支持默认构造函数和复制构造函数,如string......
  • select()函数以及FD_ZERO、FD_SET、FD_CLR、FD_ISSET
    select函数用于在非阻塞中,当一个套接字或一组套接字有信号时通知你,系统提供select函数来实现多路复用输入/输出模型,原型: intselect(intmaxfd,fd_set*rdset,fd_set*wr......
  • C/C++字符串查找函数
    C/C++string库(string.h)提供了几个字符串查找函数,如下:memchr在指定内存里定位给定字符strchr在指定字符串里定位给定字符strcspn返回在字符串str1里找到字符串s......
  • clock函数返回负值
    使用clock()函数来进行计时,时不时的返回一个很大的负数,怎么检查也检查不出错误,现在找出错误原因,给大家分享一下。来源网页:http://kebe-jea.blogbus.com/logs/33603387.ht......
  • Python函数用法和底层分析
    目录Python函数用法和底层分析函数的基本概念Python函数的分类核心要点形参和实参文档字符串(函数的注释)返回值函数也是对象,内存底层分析变量的作用域(全局变量和局部变......
  • OpenCV中Denoising相关函数的简单介绍
    参考:http://wenhuix.github.io/research/denoise.html一、基本情况      (一)基本方法     Fast Non-Local MeansDenoising(FNLMD),论文为  ......
  • Vue之Render函数
    关于不同版本的Vue:   1.vue.js与vue.runtime.xxx.js的区别:      (1).vue.js是完整版的Vue,包含:核心功能+模板解析器。      (2).vue.runt......
  • 特征点寻找的基础数据结构和函数
       当进行跟踪时或者其他类型的用到关键点及其描述符的分析时,通常需要做三件事情:第一个是根据一些关键点的定义搜索图像并查找该图像中的所有关键点;第二个是为发现......
  • 内联函数和重载函数
    内联函数我们知道函数调用是有时间开销的,当函数本身只有几条语句,而函数本身却被反复执行很多次时,函数调用的开销就会显得比较大,为了减小这种函数调用的开销,就引入了内联......
  • C++11:支持函数模板的默认模板参数
    在C++98/03标准中,类模板可以有默认的模板参数,如下:template<typenameT,typenameU=int,UN=0>structFoo{//...};但是却不支持函数的默认模板参数:te......