Created
June 5, 2017 05:47
-
-
Save jiraiyame/04473eeef504e840e77c46af2d6e730a to your computer and use it in GitHub Desktop.
responsive layout powered by flickr's justified layout
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import query from 'lib/query'; | |
import event from 'lib/event'; | |
import matchMedia from 'lib/matchMedia'; | |
import throttle from 'lib/throttle'; | |
import layout from 'justified-layout'; | |
// https://www.h5jun.com/post/array-shuffle.html | |
function shuffle(arr) { | |
const len = arr.length; | |
for (let i = 0; i < len - 1; i++) { | |
const idx = Math.floor(Math.random() * (len - i)); | |
const temp = arr[idx]; | |
arr[idx] = arr[len - i - 1]; | |
arr[len - i - 1] = temp; | |
} | |
return arr; | |
} | |
const photoCache = {}; | |
class Gallery { | |
constructor(options) { | |
this.options = Object.assign({}, Gallery.defaultOptions, options); | |
} | |
init() { | |
const { container, autoplay, animDelay, dataset } = this.options; | |
const photoView = query(container); | |
this.dataset = dataset; | |
this.photoView = photoView; | |
this.resizeLayout(); | |
this.updateViewHeight(); | |
photoView.style.position = 'relative'; | |
photoView.appendChild(this.renderPhotos()); | |
if (autoplay) { | |
if (this.timer) { | |
clearInterval(this.timer); | |
} | |
this.timer = setInterval(this.reorderPhotos.bind(this), animDelay); | |
} | |
this.handleResize(); | |
} | |
updateViewHeight() { | |
this.photoView.style.height = `${this.geometry.containerHeight}px`; | |
} | |
handleResize() { | |
event.on(window, 'resize', throttle(() => { | |
this.resizeLayout(() => { | |
this.renderPhotos(); | |
this.updateViewHeight(); | |
}); | |
})); | |
} | |
resizeLayout(callback) { | |
const { ratios, targetRowHeight, scaleRatios } = this.options; | |
let responsiveRowHeight = targetRowHeight; | |
Object.keys(scaleRatios).forEach(breakpoint => { | |
if (matchMedia(breakpoint).matches) { | |
responsiveRowHeight = targetRowHeight * scaleRatios[breakpoint]; | |
} | |
}); | |
this.geometry = layout(ratios, { | |
containerWidth: this.photoView.clientWidth, | |
targetRowHeight: responsiveRowHeight, | |
}); | |
if (callback) { | |
callback(); | |
} | |
} | |
reorderPhotos() { | |
this.dataset = shuffle(this.options.dataset); | |
this.renderPhotos(); | |
} | |
renderPhotos() { | |
const { photoItemClass } = this.options; | |
const fragment = document.createDocumentFragment(); | |
this.geometry.boxes.forEach((box, i) => { | |
let photo; | |
if (!photoCache[i]) { | |
photo = document.createElement('div'); | |
photo.className = photoItemClass; | |
photoCache[i] = photo; | |
fragment.appendChild(photo); | |
} else { | |
photo = photoCache[i]; | |
} | |
photo.style.width = `${box.width}px`; | |
photo.style.height = `${box.height}px`; | |
photo.style.transform = `translate(${box.left}px, ${box.top}px)`; | |
this.renderPhotoContent(photo, this.dataset[i]); | |
}); | |
return fragment; | |
} | |
renderPhotoContent(photo, data) { | |
const { baseUrl, photoField, children, matchAttrs } = this.options; | |
photo.style.backgroundImage = `url(${baseUrl}/${data[photoField]})`; | |
let itemTpl = query(children).textContent; | |
if (Array.isArray(matchAttrs)) { | |
matchAttrs.forEach(attr => { | |
itemTpl = itemTpl.replace(`{${attr}}`, data[attr]); | |
}); | |
photo.innerHTML = itemTpl; | |
} else { | |
throw new Error('Gallery: `matchAttrs` parameter should be an Array.'); | |
} | |
} | |
} | |
Gallery.defaultOptions = { | |
container: '#photoView', | |
targetRowHeight: 300, | |
scaleRatios: { | |
'(max-width: 544px)': 0.5, | |
'(min-width: 545px) and (max-width: 768px)': 0.65, | |
'(min-width: 769px)': 0.8, | |
}, | |
dataset: [], | |
ratios: [], | |
baseUrl: './', | |
photoItemClass: 'photo-view', | |
photoField: 'photo_url', | |
children: '#photoItemTemplate', | |
matchAttrs: ['title', 'summary'], | |
autoplay: true, | |
animDelay: 5 * 1000, | |
}; | |
export default Gallery; |
Author
jiraiyame
commented
Jun 5, 2017
Hello @jiraiyame,
Thanks to make this Gist
public, it is exactly what I was looking for.
Coding is absolutely not my job but I understand a bit.
I would like to know if I could adapt your script to plain html
(I rendered from php)
The base structure looks like this:
<div class="photo-gallery">
<div class="photo-card" data-width="{{ photo_width }}" data-height="{{ photo_height }}">
<img src="{{ photo_thumbnail }}"/>
</div>
// ... repeat this for each images on the pages
</div>
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment