Advertisement
artemsemkin

Asli template: mask fix

May 9th, 2023
916
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. export default class Mask extends BaseComponent {
  2.   constructor({
  3.     name,
  4.     loadInnerComponents,
  5.     parent,
  6.     element,
  7.   }) {
  8.     super({
  9.       name,
  10.       loadInnerComponents,
  11.       parent,
  12.       element,
  13.       // Component default options
  14.       defaults: {
  15.         scaleX: 1,
  16.         scaleY: 1,
  17.         autoParentRelative: true,
  18.         clone: true,
  19.         color: 'var(--color-accent-dark-theme)'
  20.       },
  21.       // Component inner elements
  22.       innerElements: {
  23.         target: '.js-mask__target',
  24.         source: '.js-mask__source'
  25.       },
  26.     });
  27.  
  28.     this.rect = {
  29.       source: {
  30.         width: 0,
  31.         height: 0,
  32.         top: 0,
  33.         right: 0,
  34.         bottom: 0,
  35.         left: 0,
  36.       },
  37.       target: {
  38.         width: 0,
  39.         height: 0,
  40.         top: 0,
  41.         right: 0,
  42.         bottom: 0,
  43.         left: 0,
  44.       }
  45.     };
  46.     this.props = {
  47.       offsetLeft: 0,
  48.       offsetTop: 0,
  49.       borderRadius: 0,
  50.       zIndex: 0
  51.     };
  52.     this._handlers = {
  53.       updateMask: app.utilities.debounce(this._updateMaskClipPath.bind(this), app.utilities.getDebounceTime()),
  54.       repaintMask: this._repaintMask.bind(this)
  55.     };
  56.  
  57.     this.maskedEl;
  58.     this.scaleX = this.options.scaleX || 1;
  59.     this.scaleY = this.options.scaleY || 1;
  60.  
  61.     this.setup();
  62.   }
  63.  
  64.   init() {
  65.     if (this.elements.source[0] && this.elements.target[0]) {
  66.       this.updateRef('preloaderRef', 'Preloader');
  67.  
  68.       if (!!this.options.clone) {
  69.         this._cloneTarget();
  70.       } else {
  71.         this.maskedEl = this.elements.target[0];
  72.       }
  73.  
  74.       this._updateMaskClipPath();
  75.       this._attachEvents();
  76.     }
  77.   }
  78.  
  79.   destroy() {
  80.     this._detachEvents();
  81.     this._clean();
  82.   }
  83.  
  84.   update() {
  85.     this._updateMaskClipPath();
  86.   }
  87.  
  88.   _attachEvents() {
  89.     this.resizeInstance = new ResizeObserver(this._handlers.updateMask);
  90.     this.resizeInstance.observe(this.element);
  91.     this.resizeInstance.observe(this.elements.source[0]);
  92.  
  93.     if (this.parent && this.parent.element) {
  94.       this.parent.element.addEventListener('animation/update', this._handlers.repaintMask);
  95.       this.parent.element.addEventListener('animation/start', this._handlers.updateMask);
  96.     }
  97.  
  98.     if (this.preloaderRef) {
  99.       this.preloaderRef.loaded.then(this._handlers.updateMask);
  100.     }
  101.   }
  102.  
  103.   _detachEvents() {
  104.     if (this.resizeInstance) {
  105.       this.resizeInstance.disconnect();
  106.       this.resizeInstance = null;
  107.     }
  108.  
  109.     if (this.parent && this.parent.element) {
  110.       this.parent.element.removeEventListener('animation/update', this._handlers.repaintMask);
  111.       this.parent.element.removeEventListener('animation/start', this._handlers.updateMask);
  112.     }
  113.   }
  114.  
  115.   _cloneTarget() {
  116.     const
  117.       parentEl = this.elements.target[0].parentElement,
  118.       commentNodeBefore = document.createComment(` ### Mask Clone ### `),
  119.       commentNodeAFter = document.createComment(` ### - Mask Clone ### `);
  120.  
  121.     this.destroySplitText();
  122.     this.maskedEl = this.elements.target[0].cloneNode(true);
  123.     this.maskedEl.classList.add('js-mask__clone');
  124.     this.maskedEl.classList.remove(this.innerSelectors.target.replace('.', ''));
  125.     this.maskedEl.style.position = 'absolute';
  126.  
  127.     if (typeof this.options.color === 'string') {
  128.       this.maskedEl.style.color = this.options.color;
  129.       this.maskedEl.classList.add('js-mask__clone_has-color');
  130.     }
  131.  
  132.     if (!!this.options.autoParentRelative) {
  133.       if (parentEl && window.getComputedStyle(parentEl).position !== 'relative') {
  134.         parentEl.style.position = 'relative';
  135.       }
  136.     }
  137.  
  138.     this.elements.target[0].after(this.maskedEl);
  139.     this._initSplitText();
  140.     this.maskedEl.before(commentNodeBefore);
  141.     this.maskedEl.after(commentNodeAFter);
  142.   }
  143.  
  144.   _updateMaskClipPath() {
  145.     this._clean();
  146.     this._updateRect();
  147.     this._updateProps();
  148.     this.setMask();
  149.   }
  150.  
  151.   _updateProps() {
  152.     Object.assign(this.props, {
  153.       offsetLeft: this.elements.target[0].offsetLeft,
  154.       offsetTop: this.elements.target[0].offsetTop,
  155.       borderRadiusPercent: gsap.getProperty(this.elements.source[0], 'borderRadius'),
  156.       borderRadiusPixels: gsap.getProperty(this.elements.source[0], 'borderRadius', 'px'),
  157.       zIndex: gsap.getProperty(this.elements.target[0], 'zIndex') + 1,
  158.     });
  159.   }
  160.  
  161.   _clean() {
  162.     gsap.set(this.maskedEl, {
  163.       clearProps: 'top,left,width,height,margin,zIndex,pointerEvents,clipPath,transform'
  164.     });
  165.   }
  166.  
  167.   _updateRect() {
  168.     Object.assign(this.rect, {
  169.       target: this.elements.target[0].getBoundingClientRect(),
  170.       source: this.elements.source[0].getBoundingClientRect()
  171.     });
  172.   }
  173.  
  174.   _repaintMask() {
  175.     if (this.maskedEl) {
  176.       const savedClipPath = this.maskedEl.style.clipPath;
  177.  
  178.       this.maskedEl.style.clipPath = 'none';
  179.       this.maskedEl.offsetWidth;
  180.       this.maskedEl.style.clipPath = savedClipPath;
  181.     }
  182.   }
  183.  
  184.   setMask() {
  185.     let vars = {
  186.       position: 'absolute',
  187.       top: this.props.offsetTop,
  188.       left: this.props.offsetLeft,
  189.       width: this.rect.target.width,
  190.       height: this.rect.target.height,
  191.       margin: 0,
  192.       pointerEvents: 'none',
  193.       zIndex: this.props.zIndex
  194.     };
  195.  
  196.     const
  197.       radiusX = this.rect.source.width / 2,
  198.       radiusY = this.rect.source.height / 2;
  199.  
  200.     // Oval shape
  201.     if (this.props.borderRadiusPercent === 100) {
  202.       const
  203.         positionX = this.rect.source.left - this.rect.target.left + radiusX,
  204.         positionY = this.rect.source.top - this.rect.target.top + radiusY;
  205.  
  206.       vars['clipPath'] = `ellipse(${radiusX}px ${radiusY}px at ${positionX}px ${positionY}px)`;
  207.     } else { // Rectangle shape
  208.       const
  209.         offsetX = radiusX - radiusX * this.scaleX,
  210.         offsetY = radiusY - radiusY * this.scaleY,
  211.         top = this.rect.source.top - this.rect.target.top + offsetY,
  212.         right = this.rect.target.right - this.rect.source.right + offsetX,
  213.         bottom = this.rect.target.bottom - this.rect.source.bottom + offsetY,
  214.         left = this.rect.source.left - this.rect.target.left + offsetX;
  215.  
  216.       vars['clipPath'] = `inset(${top}px ${right}px ${bottom}px ${left}px round ${this.props.borderRadiusPixels})`;
  217.     }
  218.  
  219.     gsap.set(this.maskedEl, vars);
  220.   }
  221. }
  222.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement