aboutsummaryrefslogtreecommitdiff
path: root/src/site/components/grid/waterfall/Waterfall.vue
diff options
context:
space:
mode:
authorKana <[email protected]>2020-12-24 21:41:24 +0900
committerGitHub <[email protected]>2020-12-24 21:41:24 +0900
commit2412a60bd4cb2364a477a3af79a8c6dcb6b0ddab (patch)
treedbf2b2cad342f31849a62089dedd40165758af86 /src/site/components/grid/waterfall/Waterfall.vue
parentEnable deleting files with the API key (diff)
parentbug: fix showlist resetting itself every time the page is changed (diff)
downloadhost.fuwn.me-2412a60bd4cb2364a477a3af79a8c6dcb6b0ddab.tar.xz
host.fuwn.me-2412a60bd4cb2364a477a3af79a8c6dcb6b0ddab.zip
Merge pull request #228 from Zephyrrus/begone_trailing_commas
Merge own dev branch into main dev branch
Diffstat (limited to 'src/site/components/grid/waterfall/Waterfall.vue')
-rw-r--r--src/site/components/grid/waterfall/Waterfall.vue254
1 files changed, 104 insertions, 150 deletions
diff --git a/src/site/components/grid/waterfall/Waterfall.vue b/src/site/components/grid/waterfall/Waterfall.vue
index 8631ea5..af2af3f 100644
--- a/src/site/components/grid/waterfall/Waterfall.vue
+++ b/src/site/components/grid/waterfall/Waterfall.vue
@@ -1,180 +1,134 @@
-<style>
- .waterfall {
- position: relative;
- }
-</style>
<template>
- <div class="waterfall">
- <slot />
+ <div ref="waterfall" class="waterfall">
+ <WaterfallItem
+ v-for="item in items"
+ :key="item.id"
+ :style="{ width: `${itemWidth}px`, marginBottom: `${gutterHeight}px` }"
+ :width="itemWidth">
+ <slot :item="item" />
+ </WaterfallItem>
</div>
</template>
<script>
-// import {quickSort, getMinIndex, _, sum} from './util'
+import WaterfallItem from './WaterfallItem.vue';
-const quickSort = (arr, type) => {
- const left = [];
- const right = [];
- if (arr.length <= 1) {
- return arr;
- }
- const povis = arr[0];
- for (let i = 1; i < arr.length; i++) {
- if (arr[i][type] < povis[type]) {
- left.push(arr[i]);
- } else {
- right.push(arr[i]);
- }
- }
- return quickSort(left, type).concat(povis, quickSort(right, type))
-};
+const isBrowser = typeof window !== 'undefined';
+// eslint-disable-next-line global-require
+const Masonry = isBrowser ? window.Masonry || require('masonry-layout') : null;
+const imagesloaded = isBrowser ? require('imagesloaded') : null;
-const getMinIndex = arr => {
- let pos = 0;
- for (let i = 0; i < arr.length; i++) {
- if (arr[pos] > arr[i]) {
- pos = i;
- }
- }
- return pos;
-};
-
-const _ = {
- on(el, type, func, capture = false) {
- el.addEventListener(type, func, capture);
- },
- off(el, type, func, capture = false) {
- el.removeEventListener(type, func, capture);
- }
-};
-
-const sum = arr => arr.reduce((sum, val) => sum + val);
export default {
name: 'Waterfall',
+ components: {
+ WaterfallItem
+ },
props: {
- gutterWidth: {
- type: Number,
- default: 0
- },
- gutterHeight: {
- type: Number,
- default: 0
- },
- resizable: {
- type: Boolean,
- default: true
+ options: {
+ type: Object,
+ default: () => {}
},
- align: {
- type: String,
- default: 'center'
+ items: {
+ type: Array,
+ default: () => []
},
- fixWidth: {
- type: Number
- },
- minCol: {
+ itemWidth: {
type: Number,
- default: 1
+ default: 150
},
- maxCol: {
- type: Number
+ gutterWidth: {
+ type: Number,
+ default: 10
},
- percent: {
- type: Array
+ gutterHeight: {
+ type: Number,
+ default: 4
}
},
- data() {
- return {
- timer: null,
- colNum: 0,
- lastWidth: 0,
- percentWidthArr: []
- };
+ mounted() {
+ this.initializeMasonry();
+ this.imagesLoaded();
},
- created() {
- this.$on('itemRender', () => {
- if (this.timer) {
- clearTimeout(this.timer);
- }
- this.timer = setTimeout(() => {
- this.render();
- }, 0);
- });
+ updated() {
+ this.performLayout();
+ this.imagesLoaded();
},
- mounted() {
- this.resizeHandle();
- this.$watch('resizable', this.resizeHandle);
+ unmounted() {
+ this.masonry.destroy();
},
methods: {
- calulate(arr) {
- let pageWidth = this.fixWidth ? this.fixWidth : this.$el.offsetWidth;
- // 百分比布局计算
- if (this.percent) {
- this.colNum = this.percent.length;
- const total = sum(this.percent);
- this.percentWidthArr = this.percent.map(value => (value / total) * pageWidth);
- this.lastWidth = 0;
- // 正常布局计算
- } else {
- this.colNum = parseInt(pageWidth / (arr.width + this.gutterWidth));
- if (this.minCol && this.colNum < this.minCol) {
- this.colNum = this.minCol;
- this.lastWidth = 0;
- } else if (this.maxCol && this.colNum > this.maxCol) {
- this.colNum = this.maxCol;
- this.lastWidth = pageWidth - (arr.width + this.gutterWidth) * this.colNum + this.gutterWidth;
- } else {
- this.lastWidth = pageWidth - (arr.width + this.gutterWidth) * this.colNum + this.gutterWidth;
+ imagesLoaded() {
+ const node = this.$refs.waterfall;
+ imagesloaded(
+ node,
+ () => {
+ this.masonry.layout();
}
+ );
+ },
+ performLayout() {
+ const diff = this.diffDomChildren();
+ if (diff.removed.length > 0) {
+ this.masonry.remove(diff.removed);
+ this.masonry.reloadItems();
+ }
+ if (diff.appended.length > 0) {
+ this.masonry.appended(diff.appended);
+ this.masonry.reloadItems();
}
+ if (diff.prepended.length > 0) {
+ this.masonry.prepended(diff.prepended);
+ }
+ if (diff.moved.length > 0) {
+ this.masonry.reloadItems();
+ }
+ this.masonry.layout();
},
- resizeHandle() {
- if (this.resizable) {
- _.on(window, 'resize', this.render, false);
- } else {
- _.off(window, 'resize', this.render, false);
+ diffDomChildren() {
+ const oldChildren = this.domChildren.filter((element) => !!element.parentNode);
+ const newChildren = this.getNewDomChildren();
+ const removed = oldChildren.filter((oldChild) => !newChildren.includes(oldChild));
+ const domDiff = newChildren.filter((newChild) => !oldChildren.includes(newChild));
+ const prepended = domDiff.filter((newChild, index) => newChildren[index] === newChild);
+ const appended = domDiff.filter((el) => !prepended.includes(el));
+ let moved = [];
+ if (removed.length === 0) {
+ moved = oldChildren.filter((child, index) => index !== newChildren.indexOf(child));
}
+ this.domChildren = newChildren;
+ return {
+ old: oldChildren,
+ new: newChildren,
+ removed,
+ appended,
+ prepended,
+ moved
+ };
},
- render() {
- // 重新排序
- let childArr = [];
- childArr = this.$children.map(child => child.getMeta());
- childArr = quickSort(childArr, 'order');
- // 计算列数
- this.calulate(childArr[0])
- let offsetArr = Array(this.colNum).fill(0);
- // 渲染
- childArr.forEach(child => {
- let position = getMinIndex(offsetArr);
- // 百分比布局渲染
- if (this.percent) {
- let left = 0;
- child.el.style.width = `${this.percentWidthArr[position]}px`;
- if (position === 0) {
- left = 0;
- } else {
- for (let i = 0; i < position; i++) {
- left += this.percentWidthArr[i];
- }
- }
- child.el.style.left = `${left}px`;
- // 正常布局渲染
- } else {
- if (this.align === 'left') { // eslint-disable-line no-lonely-if
- child.el.style.left = `${position * (child.width + this.gutterWidth)}px`;
- } else if (this.align === 'right') {
- child.el.style.left = `${position * (child.width + this.gutterWidth) + this.lastWidth}px`;
- } else {
- child.el.style.left = `${position * (child.width + this.gutterWidth) + this.lastWidth / 2}px`;
+ initializeMasonry() {
+ if (!this.masonry) {
+ this.masonry = new Masonry(
+ this.$refs.waterfall,
+ {
+ columnWidth: this.itemWidth,
+ gutter: this.gutterWidth,
+ ...this.options
}
- }
- if (child.height === 0) {
- return;
- }
- child.el.style.top = `${offsetArr[position]}px`;
- offsetArr[position] += (child.height + this.gutterHeight);
- this.$el.style.height = `${Math.max.apply(Math, offsetArr)}px`;
- });
- this.$emit('rendered', this);
+ );
+ this.domChildren = this.getNewDomChildren();
+ }
+ },
+ getNewDomChildren() {
+ const node = this.$refs.waterfall;
+ const children = this.options && this.options.itemSelector
+ ? node.querySelectorAll(this.options.itemSelector) : node.children;
+ return Array.prototype.slice.call(children);
}
}
};
</script>
+
+<style lang="scss" scoped>
+.wfi {
+
+}
+</style>