PageSourceSearch

https://tianxinhuang.github.io/projects/Deface/static/js/dics.original.js

js tianxinhuang.github.io collected 2026-10-03 10:05:11 UTC 17,394 bytes, 643 lines download raw bytes

1/*
2 * Dics: Definitive image comparison slider. A multiple image vanilla comparison slider.
3 *
4 * By Abel Cabeza Román, a Codictados developer
5 * Src: https://github.com/abelcabezaroman/definitive-image-comparison-slider
6 * Example: http://codictados.com/portfolio/definitive-image-comparison-slider-demo/
7 */
8
9/**
10 *
11 */
12
13/**
14 *
15 * @type {{container: null, filters: null, hideTexts: null, textPosition: string, linesOrientation: string, rotate: number, arrayBackgroundColorText: null, arrayColorText: null, linesColor: null}}
16 */
17let defaultOptions = {
18  container: null, // **REQUIRED**: HTML container | `document.querySelector('.b-dics')` |
19  filters: null, // Array of CSS string filters  |`['blur(3px)', 'grayscale(1)', 'sepia(1)', 'saturate(3)']` |
20  hideTexts: true, // Show text only when you hover the image container |`true`,`false`|
21  textPosition: "center", // Set the prefer text position  |`'center'`,`'top'`, `'right'`, `'bottom'`, `'left'` |
22  linesOrientation: "horizontal", // Change the orientation of lines  |`'horizontal'`,`'vertical'` |
23  rotate: 0, // Rotate the image container (not too useful but it's a beatiful effect. String of rotate CSS rule)  |`'45deg'`|
24  arrayBackgroundColorText: null, // Change the bacground-color of sections texts with an array |`['#000000', '#FFFFFF']`|
25  arrayColorText: null, // Change the color of texts with an array  |`['#FFFFFF', '#000000']`|
26  linesColor: null // Change the lines and arrows color  |`'rgb(0,0,0)'`|
27
28};
29
30/**
31 *
32 * @param options
33 * @constructor
34 */
35let Dics = function(options) {
36  this.options = utils.extend({}, [defaultOptions, options], {
37    clearEmpty: true
38  }); 
39
40  this.container = this.options.container;
41
42  if (this.container == null) {
43    console.error("Container element not found!");
44  } else {
45
46    this._setOrientation(this.options.linesOrientation, this.container);
47    this.images = this._getImages();
48    this.sliders = [];
49    this._activeSlider = null;
50
51
52    this._load(this.images[0]);
53
54  }
55};
56
57
58/**
59 *
60 * @private
61 */
62Dics.prototype._load = function(firstImage, maxCounter = 100000) {
63  if (firstImage.naturalWidth) {
64    this._buidAfterFirstImageLoad(firstImage);
65    window.addEventListener("resize", () => {
66      this._setContainerWidth(firstImage);
67      this._resetSizes();
68    });
69
70  } else {
71    if (maxCounter > 0) {
72      maxCounter--;
73      setTimeout(() => {
74        this._load(firstImage, maxCounter);
75      }, 100);
76    } else {
77      console.error("error loading images");
78    }
79
80  }
81};
82
83
84/**
85 *
86 * @private
87 */
88Dics.prototype._buidAfterFirstImageLoad = function(firstImage) {
89  this._setContainerWidth(firstImage);
90
91  this._build();
92  this._setEvents();
93};
94
95
96/**
97 *
98 * @private
99 */
100Dics.prototype._setContainerWidth = function(firstImage) {
101  this.options.container.style.height = `${this._calcContainerHeight(firstImage)}px`;
102};
103
104
105/**
106 *
107 * @private
108 */
109Dics.prototype._setOpacityContainerForLoading = function(opacity) {
110  this.options.container.style.opacity = opacity;
111};
112
113
114/**
115 * Reset sizes on window size change
116 * @private
117 */
118Dics.prototype._resetSizes = function() {
119  let dics = this;
120  let imagesLength = dics.images.length;
121
122  let initialImagesContainerWidth = dics.container.getBoundingClientRect()[dics.config.sizeField] / imagesLength;
123
124  const sections$$ = dics.container.querySelectorAll("[data-function='b-dics__section']");
125  for (let i = 0; i < sections$$.length; i++) {
126    let section$$ = sections$$[i];
127
128    section$$.style.flex = `0 0 ${initialImagesContainerWidth}px`;
129
130    section$$.querySelector(".b-dics__image").style[this.config.positionField] = `${i * -initialImagesContainerWidth}px`;
131
132    const slider$$ = section$$.querySelector(".b-dics__slider");
133    if (slider$$) {
134      slider$$.style[this.config.positionField] = `${initialImagesContainerWidth * (i + 1)}px`;
135
136    }
137
138  }
139
140};
141
142/**
143 * Build HTML
144 * @private
145 */
146Dics.prototype._build = function() {
147  let dics = this;
148
149  dics._applyGlobalClass(dics.options);
150
151  let imagesLength = dics.images.length;
152
153
154  let initialImagesContainerWidth = dics.container.getBoundingClientRect()[dics.config.sizeField] / imagesLength;
155
156  for (let i = 0; i < imagesLength; i++) {
157    let image = dics.images[i];
158    let section = dics._createElement("div", "b-dics__section");
159    let imageContainer = dics._createElement("div", "b-dics__image-container");
160    let slider = dics._createSlider(i, initialImagesContainerWidth);
161
162    dics._createAltText(image, i, imageContainer);
163
164    dics._applyFilter(image, i, dics.options.filters);
165    dics._rotate(image, imageContainer);
166
167
168    section.setAttribute("data-function", "b-dics__section");
169    section.style.flex = `0 0 ${initialImagesContainerWidth}px`;
170
171    image.classList.add("b-dics__image");
172
173    section.appendChild(imageContainer);
174    imageContainer.appendChild(image);
175
176    if (i < imagesLength - 1) {
177      section.appendChild(slider);
178    }
179
180    dics.container.appendChild(section);
181
182    image.style[this.config.positionField] = `${i * -initialImagesContainerWidth}px`;
183
184
185  }
186
187  this.sections = this._getSections();
188  this._setOpacityContainerForLoading(1);
189};
190
191
192/**
193 *
194 * @returns {NodeListOf<SVGElementTagNameMap[string]>
194 | NodeListOf<HTMLElementTagNameMap[string]> | NodeListOf<Element>}
195 * @private
196 */
197Dics.prototype._getImages = function() {
198  return this.container.querySelectorAll("img");
199};
200
201
202/**
203 *
204 * @returns {NodeListOf<SVGElementTagNameMap[string]> | NodeListOf<HTMLElementTagNameMap[string]> | NodeListOf<Element>}
205 * @private
206 */
207Dics.prototype._getSections = function() {
208  return this.container.querySelectorAll("[data-function=\"b-dics__section\"]");
209};
210
211/**
212 *
213 * @param elementClass
214 * @param className
215 * @returns {HTMLElement | HTMLSelectElement | HTMLLegendElement | HTMLTableCaptionElement | HTMLTextAreaElement | HTMLModElement | HTMLHRElement | HTMLOutputElement | HTMLPreElement | HTMLEmbedElement | HTMLCanvasElement | HTMLFrameSetElement | HTMLMarqueeElement | HTMLScriptElement | HTMLInputElement | HTMLUnknownElement | HTMLMetaElement | HTMLStyleElement | HTMLObjectElement | HTMLTemplateElement | HTMLBRElement | HTMLAudioElement | HTMLIFrameElement | HTMLMapElement | HTMLTableElement | HTMLAnchorElement | HTMLMenuElement | HTMLPictureElement | HTMLParagraphElement | HTMLTableDataCellElement | HTMLTableSectionElement | HTMLQuoteElement | HTMLTableHeaderCellElement | HTMLProgressElement | HTMLLIElement | HTMLTableRowElement | HTMLFontElement | HTMLSpanElement | HTMLTableColElement | HTMLOptGroupElement | HTMLDataElement | HTMLDListElement | HTMLFieldSetElement | HTMLSourceElement | HTMLBodyElement | HTMLDirectoryElement | HTMLDivElement | HTMLUListElement | HTMLHtmlElement | HTMLAreaElement | HTMLMeterElement | HTMLAppletElement | HTMLFrameElement | HTMLOptionElement | HTMLImageElement | HTMLLinkElement | HTMLHeadingElement | HTMLSlotElement | HTMLVideoElement | HTMLBaseFontElement | HTMLTitleElement | HTMLButtonElement | HTMLHeadElement | HTMLParamElement | HTMLTrackElement | HTMLOListElement | HTMLDataListElement | HTMLLabelElement | HTMLFormElement | HTMLTimeElement | HTMLBaseElement}
216 * @private
217 */
218Dics.prototype._createElement = function(elementClass, className) {
219  let newElement = document.createElement(elementClass);
220
221  newElement.classList.add(className);
222
223  return newElement;
224};
225
226/**
227 * Set need DOM events
228 * @private
229 */
230Dics.prototype._setEvents = function() {
231  let dics = this;
232
233  dics._disableImageDrag();
234
235  dics._isGoingRight = null;
236
237  let oldx = 0;
238
239  let listener = function(event) {
240
241    let xPageCoord = event.pageX ? event.pageX : event.touches[0].pageX;
242
243    if (xPageCoord < oldx) {
244      dics._isGoingRight = false;
245    } else if (xPageCoord > oldx) {
246      dics._isGoingRight = true;
247    }
248
249    oldx = xPageCoord;
250
251    let position = dics._calcPosition(event);
252
253    let beforeSectionsWidth = dics._beforeSectionsWidth(dics.sections, dics.images, dics._activeSlider);
254
255    let calcMovePixels = position - beforeSectionsWidth;
256
257    dics.sliders[dics._activeSlider].style[dics.config.positionField] = `${position}px`;
258
259    dics._pushSections(calcMovePixels, position);
260  };
261
262  dics.container.addEventListener("click", listener);
263
264  for (let i = 0; i < dics.sliders.length; i++) {
265    let slider = dics.sliders[i];
266    utils.setMultiEvents(slider, ["mousedown", "touchstart"], function(event) {
267      dics._activeSlider = i;
268
269      dics._clickPosition = dics._calcPosition(event);
270
271      slider.classList.add("b-dics__slider--active");
272
273      utils.setMultiEvents(dics.container, ["mousemove", "touchmove"], listener);
274    });
275  }
276
277
278  let listener2 = function() {
279    let activeElements = dics.container.querySelectorAll(".b-dics__slider--active");
280
281    for (let activeElement of activeElements) {
282      activeElement.classList.remove("b-dics__slider--active");
283      utils.removeMultiEvents(dics.container, ["mousemove", "touchmove"], listener);
284    }
285  };
286
287  utils.setMultiEvents(document.body, ["mouseup", "touchend"], listener2);
288
289
290};
291
292/**
293 *
294 * @param sections
295 * @param images
296 * @param activeSlider
297 * @returns {number}
298 * @private
299 */
300Dics.prototype._beforeSectionsWidth = function(sections, images, activeSlider) {
301  let width = 0;
302  for (let i = 0; i < sections.length; i++) {
303    let section = sections[i];
304    if (i !== activeSlider) {
305      width += section.getBoundingClientRect()[this.config.sizeField];
306    } else {
307      return width;
308    }
309  }
310};
311
312/**
313 *
314 * @returns {number}
315 * @private
316 */
317Dics.prototype._calcContainerHeight = function(firstImage) {
318  let imgHeight = firstImage.naturalHeight;
319  let imgWidth = firstImage.naturalWidth;
320  let containerWidth = this.options.container.getBoundingClientRect().width;
321
322  return (containerWidth / imgWidth) * imgHeight;
323};
324
325
326/**
327 *
328 * @param sections
329 * @param images
330 * @private
331 */
332Dics.prototype._setLeftToImages = function(sections, images) {
333  let size = 0;
334  for (let i = 0; i < images.length; i++) {
335    let image = images[i];
336
337    image.style[this.config.positionField] = `-${size}px`;
338    size += sections[i].getBoundingClientRect()[this.config.sizeField];
339
340    this.sliders[i].style[this.config.positionField] = `${size}px`;
341
342  }
343};
344
345
346/**
347 *
348 * @private
349 */
350Dics.prototype._disableImageDrag = function() {
351  for (let i = 0; i < this.images.length; i++) {
352    this.sliders[i].addEventListener("dragstart", function(e) {
353      e.preventDefault();
354    });
355    this.images[i].addEventListener("dragstart", function(e) {
356      e.preventDefault();
357    });
358  }
359};
360
361/**
362 *
363 * @param image
364 * @param index
365 * @param filters
366 * @private
367 */
368Dics.prototype._applyFilter = function(image, index, filters) {
369  if (filters) {
370    image.style.filter = filters[index];
371  }
372};
373
374/**
375 *
376 * @param options
377 * @private
378 */
379Dics.prototype._applyGlobalClass = function(options) {
380  let container = options.container;
381
382
383  if (options.hideTexts) {
384    container.classList.add("b-dics--hide-texts");
385  }
386
387  if (options.linesOrientation === "vertical") {
388    container.classList.add("b-dics--vertical");
389  }
390
391  if (options.textPosition === "center") {
392    container.classList.add("b-dics--tp-center");
393  } else if (options.textPosition === "bottom") {
394    container.classList.add("b-dics--tp-bottom");
395  } else if (options.textPosition === "left") {
396    container.classList.add("b-dics--tp-left");
397  } else if (options.textPosition === "right") {
398    container.classList.add("b-dics--tp-right");
399  }
400};
401
402
403Dics.prototype._createSlider = function(i, initialImagesContainerWidth) {
404  let slider = this._createElement("div", "b-dics__slider");
405
406  if (this.options.linesColor) {
407    slider.style.color = this.options.linesColor;
408  }
409
410  slider.style[this.config.positionField] = `${initialImagesContainerWidth * (i + 1)}px`;
411
412  this.sliders.push(slider);
413
414
415  return slider;
416};
417
418
419/**
420 *
421 * @param image
422 * @param i
423 * @param imageContainer
424 * @private
425 */
426Dics.prototype._createAltText = function(image, i, imageContainer) {
427  let textContent = image.getAttribute("alt");
428  if (textContent) {
429    let text = this._createElement("p", "b-dics__text");
430
431    if (this.options.arrayBackgroundColorText) {
432      text.style.backgroundColor = this.options.arrayBackgroundColorText[i];
433    }
434    if (this.options.arrayColorText) {
435      text.style.color = this.options.arrayColorText[i];
436    }
437
438    text.appendChild(document.createTextNode(textContent));
439
440    imageContainer.appendChild(text);
441  }
442};
443
444
445/**
446 *
447 * @param image
448 * @param imageContainer
449 * @private
450 */
451Dics.prototype._rotate = function(image, imageContainer) {
452  image.style.rotate = `-${this.options.rotate}`;
453  imageContainer.style.rotate = this.options.rotate;
454
455};
456
457
458/**
459 *
460 * @private
461 */
462Dics.prototype._removeActiveElements = function() {
463  let activeElements = Dics.container.querySelectorAll(".b-dics__slider--active");
464
465  for (let activeElement of activeElements) {
466    activeElement.classList.remove("b-dics__slider--active");
467    utils.removeMultiEvents(Dics.container, ["mousemove", "touchmove"], Dics.prototype._removeActiveElements);
468  }
469};
470
471
472/**
473 *
474 * @param linesOrientation
475 * @private
476 */
477Dics.prototype._setOrientation = function(linesOrientation) {
478  this.config = {};
479
480  if (linesOrientation === "vertical") {
481    this.config.offsetSizeField = "offsetHeight";
482    this.config.offsetPositionField = "offsetTop";
483    this.config.sizeField = "height";
484    this.config.positionField = "top";
485    this.config.clientField = "clientY";
486    this.config.pageField = "pageY";
487  } else {
488    this.config.offsetSizeField = "offsetWidth";
489    this.config.offsetPositionField = "offsetLeft";
490    this.config.sizeField = "width";
491    this.config.positionField = "left";
492    this.config.clientField = "clientX";
493    this.config.pageField = "pageX";
494  }
495
496
497};
498
499
500/**
501 *
502 * @param event
503 * @returns {number}
504 * @private
505 */
506Dics.prototype._calcPosition = function(event) {
507  let containerCoords = this.container.getBoundingClientRect();
508  let pixel = !isNaN(event[this.config.clientField]) ? event[this.config.clientField] : event.touches[0][this.config.clientField];
509
510  return containerCoords[this.config.positionField] < pixel ? pixel - containerCoords[this.config.positionField] : 0;
511};
512
513
514/**
515 *
516 * @private
517 */
518Dics.prototype._pushSections = function(calcMovePixels, position) {
519  // if (this._rePosUnderActualSections(position)) {
520  this._setFlex(position, this._isGoingRight);
521
522  let section = this.sections[this._activeSlider];
523  let postActualSection = this.sections[this._activeSlider + 1];
524  let sectionWidth = postActualSection.getBoundingClientRect()[this.config.sizeField] - (calcMovePixels - this.sections[this._activeSlider].getBoundingClientRect()[this.config.sizeField]);
525
526
527  section.style.flex = this._isGoingRight === true ? `2 0 ${calcMovePixels}px` : `1 1 ${calcMovePixels}px`;
528  postActualSection.style.flex = this._isGoingRight === true ? ` ${sectionWidth}px` : `2 0 ${sectionWidth}px`;
529
530  this._setLeftToImages(this.sections, this.images);
531
532  // }
533};
534
535
536/**
537 *
538 * @private
539 */
540Dics.prototype._setFlex = function(position, isGoingRight) {
541  let beforeSumSectionsSize = 0;
542
543
544  for (let i = 0; i < this.sections.length; i++) {
545    let section = this.sections[i];
546    const sectionSize = section.getBoundingClientRect()[this.config.sizeField];
547
548    beforeSumSectionsSize += sectionSize;
549
550    if ((isGoingRight && position > (beforeSumSectionsSize - sectionSize) && i > this._activeSlider) || (!isGoingRight && position < beforeSumSectionsSize) && i < this._activeSlider) {
551      section.style.flex = `1 100 ${sectionSize}px`;
552    } else {
553      section.style.flex = `0 0 ${sectionSize}px`;
554    }
555
556  }
557};
558
559
560/**
561 *
562 * @type {{extend: (function(*=, *, *): *), setMultiEvents: setMultiEvents, removeMultiEvents: removeMultiEvents, getConstructor: (function(*=): string)}}
563 */
564let utils = {
565
566
567  /**
568   * Native extend object
569   * @param target
570   * @param objects
571   * @param options
572   * @returns {*}
573   */
574  extend: function(target, objects, options) {
575
576    for (let object in objects) {
577      if (objects.hasOwnProperty(object)) {
578        recursiveMerge(target, objects[object]);
579      }
580    }
581
582    function recursiveMerge (target, object) {
583      for (let property in object) {
584        if (object.hasOwnProperty(property)) {
585          let current = object[property];
586          if (utils.getConstructor(current) === "Object") {
587            if (!target[property]) {
588              target[property] = {};
589            }
590            recursiveMerge(target[property], current);
591          } else {
592            // clearEmpty
593            if (options.clearEmpty) {
594              if (current == null) {
595                continue;
596              }
597            }
598            target[property] = current;
599          }
600        }
601      }
602    }
603
604    return target;
605  },
606
607
608  /**
609   * Set Multi addEventListener
610   * @param element
611   * @param events
612   * @param func
613   */
614  setMultiEvents: function(element, events, func) {
615    for (let i = 0; i < events.length; i++) {
616      element.addEventListener(events[i], func);
617    }
618  },
619
620
621  /**
622   *
623   * @param element
624   * @param events
625   * @param func
626   */
627  removeMultiEvents: function(element, events, func) {
628    for (let i = 0; i < events.length; i++) {
629      element.removeEventListener(events[i], func, false);
630    }
631  },
632
633
634  /**
635   * Get object constructor
636   * @param object
637   * @returns {string}
638   */
639  getConstructor: function(object) {
640    return Object.prototype.toString.call(object).slice(8, -1);
641  }
642};
643

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.