summaryrefslogtreecommitdiff
path: root/src/shadow.js
blob: dc83e74535cf4442092bc299261be00039276af2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
export default class Shadow {
  zIndex = '1000'
  isOpen = false

  /**
   * ============
   * Constructors
   * ============
   */

  constructor(image) {
    this.create(image);
    this.calculateStartsAndFinals();
    return this;
  }

  create(image) {
    // create shadow image
    const shadowImage = document.createElement('img');
    shadowImage.src = image.src;
    shadowImage.id = `libra-shadow-${image.id}`;
    shadowImage.classList.add('libra-shadow');

    // style and position shadow image
    shadowImage.style.top = image.offsetTop;
    shadowImage.style.left = image.offsetLeft;
    shadowImage.style.width = image.offsetWidth;
    shadowImage.style.height = image.offsetHeight;
    shadowImage.style.zIndex = this.zIndex;

    document.body.appendChild(shadowImage);
    this.element = shadowImage;
  }

  calculateStartsAndFinals() {
    // get starting state
    const s = {
      top: this.element.offsetTop,
      left: this.element.offsetLeft,
      width: this.element.offsetWidth,
      height: this.element.offsetHeight
    }

    const f = {};
    const margin = 50;

    // calculate final size
    const nw = this.element.naturalWidth;
    const nh = this.element.naturalHeight;
    const ratio = nw / nh;

    const ww = window.innerWidth - margin*2;
    const wh = window.innerHeight - margin*2;

    f.width = Math.min(nw, ww);
    f.height = f.width / ratio;

    if (f.height >= wh) {
      f.height = wh;
      f.width = f.height * ratio;
    }

    // calculate final position
    f.left = (ww - f.width) / 2 + margin;
    f.top = (wh - f.height) / 2 + margin;

    this.finalState = f;
    this.startingState = s;
  }

  /**
   * ============
   * Actions
   * ============
   */

  open() {
    this.isOpen = true;
    this.animate(this.startingState, this.finalState)
  }

  close() {
    this.isOpen = false;
    this.animate(this.finalState, this.startingState);
  }

  animate(starts, finals) {
    // Set up the interval for smooth transition
    const steps = 20; // Number of steps for smoothness
    let step = 0;

    // Using requestAnimationFrame for smoother animation
    const frame = () => {
      // Calculate the progress (from 0 to 1)
      const progress = step / steps;

      // Interpolate the current values based on progress
      const currentTop = starts.top + (finals.top - starts.top) * progress;
      const currentLeft = starts.left + (finals.left - starts.left) * progress;
      const currentWidth = starts.width + (finals.width - starts.width) * progress;
      const currentHeight = starts.height + (finals.height - starts.height) * progress;

      // Update the element's style
      this.element.style.top = `${currentTop}px`;
      this.element.style.left = `${currentLeft}px`;
      this.element.style.width = `${currentWidth}px`;
      this.element.style.height = `${currentHeight}px`;

      // Increase the step count
      step++;

      // Continue animating until we reach the final step
      if (step <= steps) {
        requestAnimationFrame(frame);
      } else {
        this.element.classList.toggle('open');
        if (!this.isOpen) this.destroyItself();
      }
    };

    // Start the animation
    requestAnimationFrame(frame);
  }

  placeItself() {
    document.body.appendChild(this.element);
  }

  destroyItself() {
    if (!document.body.contains(this.element)) return;
    document.body.removeChild(this.element);
  }
}