├── .gitignore ├── .npmignore ├── demo ├── src │ ├── component │ │ ├── User.vue │ │ ├── Gallery.vue │ │ ├── Welcome.vue │ │ ├── Remove.vue │ │ ├── App.vue │ │ └── Setting.vue │ ├── lib │ │ ├── img │ │ │ ├── demo_0.jpg │ │ │ ├── demo_1.jpg │ │ │ ├── demo_2.jpg │ │ │ ├── demo_3.jpg │ │ │ ├── demo_4.jpg │ │ │ ├── demo_5.jpg │ │ │ ├── demo_6.jpg │ │ │ ├── backtotop.png │ │ │ └── clipPath.svg │ │ ├── pxgallery.scss │ │ └── pxgallery.js │ ├── reset.scss │ ├── store.js │ ├── main.js │ └── variable.scss ├── .gitignore ├── .babelrc ├── index.html ├── README.md ├── package.json └── webpack.config.js ├── .travis.yml ├── package.json ├── LICENSE ├── pxgallery.min.css ├── pxgallery.scss ├── readme.md └── pxgallery.js /.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /demo/src/component/User.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /node_modules/ 3 | npm-debug.log 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - stable 5 | -------------------------------------------------------------------------------- /demo/src/lib/img/demo_0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenYuysy/PXGallery/HEAD/demo/src/lib/img/demo_0.jpg -------------------------------------------------------------------------------- /demo/src/lib/img/demo_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenYuysy/PXGallery/HEAD/demo/src/lib/img/demo_1.jpg -------------------------------------------------------------------------------- /demo/src/lib/img/demo_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenYuysy/PXGallery/HEAD/demo/src/lib/img/demo_2.jpg -------------------------------------------------------------------------------- /demo/src/lib/img/demo_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenYuysy/PXGallery/HEAD/demo/src/lib/img/demo_3.jpg -------------------------------------------------------------------------------- /demo/src/lib/img/demo_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenYuysy/PXGallery/HEAD/demo/src/lib/img/demo_4.jpg -------------------------------------------------------------------------------- /demo/src/lib/img/demo_5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenYuysy/PXGallery/HEAD/demo/src/lib/img/demo_5.jpg -------------------------------------------------------------------------------- /demo/src/lib/img/demo_6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenYuysy/PXGallery/HEAD/demo/src/lib/img/demo_6.jpg -------------------------------------------------------------------------------- /demo/src/lib/img/backtotop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenYuysy/PXGallery/HEAD/demo/src/lib/img/backtotop.png -------------------------------------------------------------------------------- /demo/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-2"], 3 | "plugins": ["transform-runtime"], 4 | "comments": false 5 | } 6 | -------------------------------------------------------------------------------- /demo/src/component/Gallery.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | 11 | 14 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | gallery_app 6 | 7 | 8 |
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /demo/src/reset.scss: -------------------------------------------------------------------------------- 1 | // reset css 2 | 3 | body, div, dl, dt, dd, ul, ol, li, 4 | h1, h2, h3, h4, h5, h6, pre, code, 5 | form, fieldset, legend, input, button, 6 | textarea, p, blockquote, th, td { 7 | margin: 0; 8 | padding: 0; 9 | } 10 | 11 | body { 12 | font-family: Helvetica, Tahoma, Arial, STXihei, "华文细黑", "Microsoft YaHei", "微软雅黑", SimSun, "宋体", Heiti, "黑体", sans-serif; 13 | } -------------------------------------------------------------------------------- /demo/src/lib/img/clipPath.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /demo/README.md: -------------------------------------------------------------------------------- 1 | # gallery_app 2 | 3 | > A Vue.js project 4 | 5 | ## Build Setup 6 | 7 | ``` bash 8 | # install dependencies 9 | npm install 10 | 11 | # serve with hot reload at localhost:8080 12 | npm run dev 13 | 14 | # build for production with minification 15 | npm run build 16 | ``` 17 | 18 | For detailed explanation on how things work, consult the [docs for vue-loader](http://vuejs.github.io/vue-loader). 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pxgallery", 3 | "version": "1.0.0", 4 | "description": "A easy to use javascript plugin for different image layouts.", 5 | "main": "pxgallery.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/StevenYuysy/PXGallery.git" 9 | }, 10 | "keywords": [ 11 | "JavaScript", 12 | "Plugin" 13 | ], 14 | "author": "stevenYuysy", 15 | "license": "MIT", 16 | "bugs": { 17 | "url": "https://github.com/StevenYuysy/PXGallery/issues" 18 | }, 19 | "homepage": "https://github.com/StevenYuysy/PXGallery#readme" 20 | } 21 | -------------------------------------------------------------------------------- /demo/src/store.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | var store = {} 4 | 5 | export default store 6 | 7 | store.url = [ 8 | "http://placehold.it/1300x1600/E97452/fff", 9 | "http://placehold.it/1300x1300/4C6EB4/fff", 10 | "http://placehold.it/1300x1250/449F93/fff", 11 | "http://placehold.it/800x400/936FBC/fff", 12 | // "http://placehold.it/1000x500/D25064/fff", 13 | "http://placehold.it/1300x1200/CF364A/fff" 14 | ]; 15 | 16 | store.options = { 17 | layout : 4, 18 | puzzleHeight: 400, 19 | coulumn : 5, 20 | heightMin : 150, 21 | gutter : 10, 22 | mdSquareSize: 2, 23 | smSquareSize: 4, 24 | fullscreenState : true 25 | } 26 | 27 | -------------------------------------------------------------------------------- /demo/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import Welcome from './component/Welcome.vue' 4 | import App from './component/App.vue' 5 | import Setting from './component/Setting.vue' 6 | import Remove from './component/Remove.vue' 7 | 8 | Vue.config.debug = true 9 | 10 | 11 | // install router 12 | Vue.use(Router) 13 | 14 | // routing 15 | var router = new Router() 16 | 17 | 18 | router.map({ 19 | '/':{ 20 | component: Welcome 21 | }, 22 | '/setting': { 23 | component: Setting 24 | }, 25 | '/remove': { 26 | component: Remove 27 | } 28 | }) 29 | 30 | 31 | 32 | 33 | router.start(App, '#app') -------------------------------------------------------------------------------- /demo/src/variable.scss: -------------------------------------------------------------------------------- 1 | // define color 2 | $black: #111; 3 | $nav_gray: #2f2f38; 4 | $dark_gray: #3a3a3a; 5 | $main_gray: #4a4a4a; 6 | $gray: #929292; 7 | $font_gray: #edecee; 8 | $bg_gray: #f8f8f8; 9 | $light_blue: #7C7EFF; 10 | $blue: #2F33FF; 11 | $gray_blue: #3E3F7F; 12 | $dark_blue: #18197F; 13 | $white: #fff; 14 | $activeColor: $bg_gray; 15 | // mixin 16 | @mixin hover { 17 | -webkit-transition-duration: 0.3s; 18 | transition-duration: 0.3s; 19 | -webkit-transform: translateZ(0); 20 | transform: translateZ(0); 21 | -webkit-transition-property: color, background-color; 22 | transition-property: color, background-color; 23 | &:hover { 24 | color: #111; 25 | background-color: $activeColor; 26 | } 27 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2016 StevenYuysy 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gallery_app", 3 | "description": "A Vue.js project", 4 | "author": "StevenYu ", 5 | "private": true, 6 | "scripts": { 7 | "dev": "webpack-dev-server --inline --hot", 8 | "build": "cross-env NODE_ENV=production webpack --progress --hide-modules" 9 | }, 10 | "dependencies": { 11 | "vue": "^1.0.0", 12 | "babel-runtime": "^6.0.0" 13 | }, 14 | "devDependencies": { 15 | "babel-core": "^6.0.0", 16 | "babel-loader": "^6.0.0", 17 | "babel-plugin-transform-runtime": "^6.0.0", 18 | "babel-preset-es2015": "^6.0.0", 19 | "babel-preset-stage-2": "^6.0.0", 20 | "cross-env": "^1.0.6", 21 | "css-loader": "^0.23.0", 22 | "file-loader": "^0.8.4", 23 | "json-loader": "^0.5.4", 24 | "node-sass": "^3.7.0", 25 | "sass-loader": "^3.2.0", 26 | "url-loader": "^0.5.7", 27 | "vue-animated-list": "^1.0.2", 28 | "vue-hot-reload-api": "^1.2.0", 29 | "vue-html-loader": "^1.0.0", 30 | "vue-loader": "^8.2.1", 31 | "vue-router": "^0.7.13", 32 | "vue-style-loader": "^1.0.0", 33 | "webpack": "^1.12.2", 34 | "webpack-dev-server": "^1.12.0" 35 | } 36 | } -------------------------------------------------------------------------------- /demo/src/component/Welcome.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 46 | -------------------------------------------------------------------------------- /demo/webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var webpack = require('webpack') 3 | 4 | module.exports = { 5 | entry: './src/main.js', 6 | output: { 7 | path: path.resolve(__dirname, './dist'), 8 | publicPath: '/dist/', 9 | filename: 'build.js' 10 | }, 11 | resolveLoader: { 12 | root: path.join(__dirname, 'node_modules'), 13 | }, 14 | module: { 15 | loaders: [ 16 | { 17 | test: /\.vue$/, 18 | loader: 'vue' 19 | }, 20 | { 21 | test: /\.js$/, 22 | loader: 'babel', 23 | exclude: /node_modules/ 24 | }, 25 | { 26 | test: /\.json$/, 27 | loader: 'json' 28 | }, 29 | { 30 | test: /\.html$/, 31 | loader: 'vue-html' 32 | }, 33 | { 34 | test: /\.(png|jpg|gif|svg)$/, 35 | loader: 'url', 36 | query: { 37 | limit: 10000, 38 | name: '[name].[ext]' 39 | } 40 | } 41 | ] 42 | }, 43 | devServer: { 44 | historyApiFallback: true, 45 | noInfo: true 46 | }, 47 | devtool: '#eval-source-map' 48 | } 49 | 50 | if (process.env.NODE_ENV === 'production') { 51 | module.exports.devtool = '#source-map' 52 | // http://vuejs.github.io/vue-loader/workflow/production.html 53 | module.exports.plugins = (module.exports.plugins || []).concat([ 54 | new webpack.DefinePlugin({ 55 | 'process.env': { 56 | NODE_ENV: '"production"' 57 | } 58 | }), 59 | new webpack.optimize.UglifyJsPlugin({ 60 | compress: { 61 | warnings: false 62 | } 63 | }), 64 | new webpack.optimize.OccurenceOrderPlugin() 65 | ]) 66 | } 67 | -------------------------------------------------------------------------------- /demo/src/component/Remove.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 46 | 47 | -------------------------------------------------------------------------------- /demo/src/component/App.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 44 | 45 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /pxgallery.min.css: -------------------------------------------------------------------------------- 1 | .pxgalleryContainer{width:80%;overflow:hidden;margin:0 auto;position:relative}.pxgalleryContainer .pxgalleryBox{position:relative;box-sizing:border-box}.puzzle-1 .pxgalleryBox{width:100%;height:100%;overflow:hidden}.puzzle-2 .pxgalleryBox:nth-child(1){position:absolute;left:0;width:66.6%;height:100%;-webkit-clip-path:polygon(0 0,100% 0,50% 100%,0 100%)}.puzzle-2 .pxgalleryBox:nth-child(2){position:absolute;right:0;width:66.6%;height:100%;left:33.333%;-webkit-clip-path:polygon(50% 0,100% 0,100% 100%,0 100%)}.puzzle-3 .pxgalleryBox:nth-child(1){float:left;width:66.6%;height:100%}.puzzle-3 .pxgalleryBox:nth-child(2),.puzzle-3 .pxgalleryBox:nth-child(3){float:left;width:33.4%;height:50%}.puzzle-4 .pxgalleryBox{float:left;width:50%;height:50%}.puzzle-5 .pxgalleryBox{position:absolute}.puzzle-5 .pxgalleryBox:nth-child(1){top:0;left:0;width:66.6%;height:66.6%}.puzzle-5 .pxgalleryBox:nth-child(2){top:0;right:0;width:33.4%;height:66.6%}.puzzle-5 .pxgalleryBox:nth-child(3){bottom:0;right:0;width:33.3%;height:33.4%}.puzzle-5 .pxgalleryBox:nth-child(4){bottom:0;left:33.333%;width:33.333%;height:33.4%}.puzzle-5 .pxgalleryBox:nth-child(5){bottom:0;left:0;width:33.333%;height:33.4%}.puzzle-6 .pxgalleryBox{float:left;width:33.3%;height:33.3%}.puzzle-6 .pxgalleryBox:nth-child(1){width:66.6%;height:66.6%}.puzzle-6 .pxgalleryBox:nth-child(2),.puzzle-6 .pxgalleryBox:nth-child(3),.puzzle-6 .pxgalleryBox:nth-child(4){float:right;width:33.4%}.puzzle-1 img,.puzzle-2 img,.puzzle-3 img,.puzzle-4 img,.puzzle-5 img,.puzzle-6 img{height:100%;width:100%;object-fit:cover;object-position:center}.waterfallColumn{float:left}.waterfallColumn img{width:100%}.barrel{white-space:nowrap}.barrel .pxgalleryBox{display:inline-block}.barrel .pxgalleryBox img{height:100%}@media only screen and (min-width:769px){.square .col-md-1{width:8.33333%;float:left;overflow:hidden}}@media only screen and (max-width:768px){.square .col-sm-1{width:8.33333%;float:left;overflow:hidden}}@media only screen and (min-width:769px){.square .col-md-2{width:16.66667%;float:left;overflow:hidden}}@media only screen and (max-width:768px){.square .col-sm-2{width:16.66667%;float:left;overflow:hidden}}@media only screen and (min-width:769px){.square .col-md-3{width:25%;float:left;overflow:hidden}}@media only screen and (max-width:768px){.square .col-sm-3{width:25%;float:left;overflow:hidden}}@media only screen and (min-width:769px){.square .col-md-4{width:33.33333%;float:left;overflow:hidden}}@media only screen and (max-width:768px){.square .col-sm-4{width:33.33333%;float:left;overflow:hidden}}@media only screen and (min-width:769px){.square .col-md-5{width:41.66667%;float:left;overflow:hidden}}@media only screen and (max-width:768px){.square .col-sm-5{width:41.66667%;float:left;overflow:hidden}}@media only screen and (min-width:769px){.square .col-md-6{width:50%;float:left;overflow:hidden}}@media only screen and (max-width:768px){.square .col-sm-6{width:50%;float:left;overflow:hidden}}@media only screen and (min-width:769px){.square .col-md-7{width:58.33333%;float:left;overflow:hidden}}@media only screen and (max-width:768px){.square .col-sm-7{width:58.33333%;float:left;overflow:hidden}}@media only screen and (min-width:769px){.square .col-md-8{width:66.66667%;float:left;overflow:hidden}}@media only screen and (max-width:768px){.square .col-sm-8{width:66.66667%;float:left;overflow:hidden}}@media only screen and (min-width:769px){.square .col-md-9{width:75%;float:left;overflow:hidden}}@media only screen and (max-width:768px){.square .col-sm-9{width:75%;float:left;overflow:hidden}}@media only screen and (min-width:769px){.square .col-md-10{width:83.33333%;float:left;overflow:hidden}}@media only screen and (max-width:768px){.square .col-sm-10{width:83.33333%;float:left;overflow:hidden}}@media only screen and (min-width:769px){.square .col-md-11{width:91.66667%;float:left;overflow:hidden}}@media only screen and (max-width:768px){.square .col-sm-11{width:91.66667%;float:left;overflow:hidden}}@media only screen and (min-width:769px){.square .col-md-12{width:100%;float:left;overflow:hidden}}@media only screen and (max-width:768px){.square .col-sm-12{width:100%;float:left;overflow:hidden}}.square .pxgalleryBox:after{content:"";display:block;padding-bottom:100%}.square .pxgalleryBox img{position:absolute;width:100%;height:100%;object-fit:cover}.pxfullscreen{position:fixed;z-index:1;top:0;width:100%;height:100%;background-color:rgba(0,0,0,.8);display:flex;flex-direction:row;justify-content:center;align-items:center}.pxfullscreen img{max-height:100%;max-width:100%}.pxfullscreen .pxnext{display:block;position:absolute;top:50%;right:30px;border-top:30px solid transparent;border-bottom:30px solid transparent;border-left:30px solid #d3d3d3;transition:.5s ease-in-out}.pxfullscreen .pxnext:hover{border-left-color:#fff}.pxfullscreen .pxprev{display:block;position:absolute;top:50%;left:30px;border-top:30px solid transparent;border-bottom:30px solid transparent;border-right:30px solid #d3d3d3;transition:.5s ease-in-out}.pxfullscreen .pxprev:hover{border-right-color:#fff}.hidden{display:none} -------------------------------------------------------------------------------- /pxgallery.scss: -------------------------------------------------------------------------------- 1 | .pxgalleryContainer { 2 | // Your custome style 3 | width: 80%; 4 | overflow: hidden; 5 | margin: 0 auto; 6 | position: relative; 7 | 8 | .pxgalleryBox { 9 | position: relative; 10 | box-sizing: border-box; 11 | } 12 | } 13 | 14 | 15 | // PUZZLE style 16 | // One image box selected 17 | .puzzle-1 { 18 | .pxgalleryBox { 19 | width: 100%; 20 | height: 100%; 21 | overflow: hidden; 22 | } 23 | } 24 | // Two image boxes selected 25 | .puzzle-2 { 26 | .pxgalleryBox:nth-child(1) { 27 | position: absolute; 28 | left: 0; 29 | width: 66.6%; 30 | height: 100%; 31 | // clip-path: url('img/clipPath.svg#clip1'); 32 | -webkit-clip-path: polygon(0% 0%,100% 0%,50% 100%,0% 100%); 33 | } 34 | .pxgalleryBox:nth-child(2) { 35 | position: absolute; 36 | right: 0; 37 | width: 66.6%; 38 | height: 100%; 39 | left: 33.333%; 40 | // clip-path: url('img/clipPath.svg#clip2'); 41 | -webkit-clip-path: polygon(50% 0%,100% 0%,100% 100%,0% 100%); 42 | } 43 | } 44 | //THree image boxes selected 45 | .puzzle-3 { 46 | .pxgalleryBox:nth-child(1) { 47 | float: left; 48 | width: 66.6%; 49 | height: 100%; 50 | } 51 | .pxgalleryBox:nth-child(2) { 52 | float: left; 53 | width: 33.4%; 54 | height: 50%; 55 | } 56 | .pxgalleryBox:nth-child(3) { 57 | float: left; 58 | width: 33.4%; 59 | height: 50%; 60 | } 61 | } 62 | //Four image boxes selected 63 | .puzzle-4 { 64 | .pxgalleryBox { 65 | float: left; 66 | width: 50%; 67 | height: 50%; 68 | } 69 | } 70 | //Five image boxes selected 71 | .puzzle-5 { 72 | .pxgalleryBox { 73 | position: absolute; 74 | } 75 | .pxgalleryBox:nth-child(1) { 76 | top: 0; 77 | left: 0; 78 | width: 66.6%; 79 | height: 66.6%; 80 | } 81 | .pxgalleryBox:nth-child(2) { 82 | top: 0; 83 | right: 0; 84 | width: 33.4%; 85 | height: 66.6%; 86 | } 87 | .pxgalleryBox:nth-child(3) { 88 | bottom: 0; 89 | right: 0; 90 | width: 33.3%; 91 | height: 33.4%; 92 | } 93 | .pxgalleryBox:nth-child(4) { 94 | bottom: 0; 95 | left: 33.333%; 96 | width: 33.333%; 97 | height: 33.4%; 98 | } 99 | .pxgalleryBox:nth-child(5) { 100 | bottom: 0; 101 | left: 0; 102 | width: 33.333%; 103 | height: 33.4%; 104 | } 105 | } 106 | //Six image boxes selected 107 | .puzzle-6 { 108 | .pxgalleryBox { 109 | float: left; 110 | width: 33.3%; 111 | height: 33.3%; 112 | } 113 | .pxgalleryBox:nth-child(1) { 114 | width: 66.6%; 115 | height: 66.6%; 116 | } 117 | .pxgalleryBox:nth-child(2), 118 | .pxgalleryBox:nth-child(3), 119 | .pxgalleryBox:nth-child(4) { 120 | float: right; 121 | width: 33.4%; 122 | } 123 | } 124 | @for $i from 1 through 6 { 125 | .puzzle-#{$i} { 126 | img { 127 | height: 100%; 128 | width: 100%; 129 | object-fit: cover; 130 | object-position: center; 131 | } 132 | } 133 | } 134 | 135 | //WATERFALL style 136 | .waterfallColumn { 137 | float: left; 138 | 139 | img { 140 | width: 100%; 141 | } 142 | } 143 | 144 | //BARREL style 145 | .barrel { 146 | white-space: nowrap; 147 | 148 | .pxgalleryBox { 149 | display: inline-block; 150 | 151 | img { 152 | height: 100%; 153 | } 154 | } 155 | } 156 | 157 | // SQUARE style 158 | .square { 159 | 160 | @for $i from 1 through 12 { 161 | .col-md-#{$i} { @media only screen and (min-width: 769px) { 162 | width: percentage(( $i / 12 )); 163 | float: left; 164 | overflow: hidden; 165 | }} 166 | 167 | .col-sm-#{$i} { @media only screen and (max-width: 768px) { 168 | width: percentage(( $i / 12 )); 169 | float: left; 170 | overflow: hidden; 171 | }} 172 | } 173 | .pxgalleryBox { 174 | 175 | &:after { 176 | content: ""; 177 | display: block; 178 | padding-bottom: 100%; 179 | } 180 | 181 | img { 182 | position: absolute; 183 | width: 100%; 184 | height: 100%; 185 | object-fit: cover; 186 | } 187 | } 188 | } 189 | 190 | //FULLSCREEN style 191 | .pxfullscreen { 192 | position: fixed; 193 | z-index: 100; 194 | top: 0; 195 | width: 100%; 196 | height: 100%; 197 | background-color: rgba(0, 0, 0, .8); 198 | display: flex; 199 | flex-direction: row; 200 | justify-content: center; 201 | align-items: center; 202 | 203 | img { 204 | max-height: 100%; 205 | max-width: 100%; 206 | } 207 | 208 | .pxnext { 209 | display: block; 210 | position: absolute; 211 | top: 50%; 212 | right: 30px; 213 | border-top: 30px solid transparent; 214 | border-bottom: 30px solid transparent; 215 | border-left: 30px solid lightgray; 216 | transition: .5s ease-in-out; 217 | 218 | &:hover { 219 | border-left-color: white; 220 | } 221 | } 222 | 223 | .pxprev{ 224 | display: block; 225 | position: absolute; 226 | top: 50%; 227 | left: 30px; 228 | border-top: 30px solid transparent; 229 | border-bottom: 30px solid transparent; 230 | border-right:30px solid lightgray; 231 | 232 | transition: .5s ease-in-out; 233 | 234 | &:hover { 235 | border-right-color: white; 236 | } 237 | } 238 | } 239 | 240 | .hidden { 241 | display: none; 242 | } 243 | 244 | -------------------------------------------------------------------------------- /demo/src/lib/pxgallery.scss: -------------------------------------------------------------------------------- 1 | .pxgalleryContainer { 2 | // Your custome style 3 | width: 80%; 4 | overflow: hidden; 5 | margin: 0 auto; 6 | position: relative; 7 | 8 | .pxgalleryBox { 9 | position: relative; 10 | box-sizing: border-box; 11 | } 12 | } 13 | 14 | 15 | // PUZZLE style 16 | // One image box selected 17 | .puzzle-1 { 18 | .pxgalleryBox { 19 | width: 100%; 20 | height: 100%; 21 | overflow: hidden; 22 | } 23 | } 24 | // Two image boxes selected 25 | .puzzle-2 { 26 | .pxgalleryBox:nth-child(1) { 27 | position: absolute; 28 | left: 0; 29 | width: 66.6%; 30 | height: 100%; 31 | // clip-path: url('img/clipPath.svg#clip1'); 32 | -webkit-clip-path: polygon(0% 0%,100% 0%,50% 100%,0% 100%); 33 | } 34 | .pxgalleryBox:nth-child(2) { 35 | position: absolute; 36 | right: 0; 37 | width: 66.6%; 38 | height: 100%; 39 | left: 33.333%; 40 | // clip-path: url('img/clipPath.svg#clip2'); 41 | -webkit-clip-path: polygon(50% 0%,100% 0%,100% 100%,0% 100%); 42 | } 43 | } 44 | //THree image boxes selected 45 | .puzzle-3 { 46 | .pxgalleryBox:nth-child(1) { 47 | float: left; 48 | width: 66.6%; 49 | height: 100%; 50 | } 51 | .pxgalleryBox:nth-child(2) { 52 | float: left; 53 | width: 33.4%; 54 | height: 50%; 55 | } 56 | .pxgalleryBox:nth-child(3) { 57 | float: left; 58 | width: 33.4%; 59 | height: 50%; 60 | } 61 | } 62 | //Four image boxes selected 63 | .puzzle-4 { 64 | .pxgalleryBox { 65 | float: left; 66 | width: 50%; 67 | height: 50%; 68 | } 69 | } 70 | //Five image boxes selected 71 | .puzzle-5 { 72 | .pxgalleryBox { 73 | position: absolute; 74 | } 75 | .pxgalleryBox:nth-child(1) { 76 | top: 0; 77 | left: 0; 78 | width: 66.6%; 79 | height: 66.6%; 80 | } 81 | .pxgalleryBox:nth-child(2) { 82 | top: 0; 83 | right: 0; 84 | width: 33.4%; 85 | height: 66.6%; 86 | } 87 | .pxgalleryBox:nth-child(3) { 88 | bottom: 0; 89 | right: 0; 90 | width: 33.3%; 91 | height: 33.4%; 92 | } 93 | .pxgalleryBox:nth-child(4) { 94 | bottom: 0; 95 | left: 33.333%; 96 | width: 33.333%; 97 | height: 33.4%; 98 | } 99 | .pxgalleryBox:nth-child(5) { 100 | bottom: 0; 101 | left: 0; 102 | width: 33.333%; 103 | height: 33.4%; 104 | } 105 | } 106 | //Six image boxes selected 107 | .puzzle-6 { 108 | .pxgalleryBox { 109 | float: left; 110 | width: 33.3%; 111 | height: 33.3%; 112 | } 113 | .pxgalleryBox:nth-child(1) { 114 | width: 66.6%; 115 | height: 66.6%; 116 | } 117 | .pxgalleryBox:nth-child(2), 118 | .pxgalleryBox:nth-child(3), 119 | .pxgalleryBox:nth-child(4) { 120 | float: right; 121 | width: 33.4%; 122 | } 123 | } 124 | @for $i from 1 through 6 { 125 | .puzzle-#{$i} { 126 | img { 127 | height: 100%; 128 | width: 100%; 129 | object-fit: cover; 130 | object-position: center; 131 | } 132 | } 133 | } 134 | 135 | //WATERFALL style 136 | .waterfallColumn { 137 | float: left; 138 | 139 | img { 140 | width: 100%; 141 | } 142 | } 143 | 144 | //BARREL style 145 | .barrel { 146 | white-space: nowrap; 147 | 148 | .pxgalleryBox { 149 | display: inline-block; 150 | 151 | img { 152 | height: 100%; 153 | } 154 | } 155 | } 156 | 157 | // SQUARE style 158 | .square { 159 | 160 | @for $i from 1 through 12 { 161 | .col-md-#{$i} { @media only screen and (min-width: 769px) { 162 | width: percentage(( $i / 12 )); 163 | float: left; 164 | overflow: hidden; 165 | }} 166 | 167 | .col-sm-#{$i} { @media only screen and (max-width: 768px) { 168 | width: percentage(( $i / 12 )); 169 | float: left; 170 | overflow: hidden; 171 | }} 172 | } 173 | .pxgalleryBox { 174 | 175 | &:after { 176 | content: ""; 177 | display: block; 178 | padding-bottom: 100%; 179 | } 180 | 181 | img { 182 | position: absolute; 183 | width: 100%; 184 | height: 100%; 185 | object-fit: cover; 186 | } 187 | } 188 | } 189 | 190 | //FULLSCREEN style 191 | .pxfullscreen { 192 | position: fixed; 193 | z-index: 100; 194 | top: 0; 195 | width: 100%; 196 | height: 100%; 197 | background-color: rgba(0, 0, 0, .8); 198 | display: flex; 199 | flex-direction: row; 200 | justify-content: center; 201 | align-items: center; 202 | 203 | img { 204 | max-height: 100%; 205 | max-width: 100%; 206 | } 207 | 208 | .pxnext { 209 | display: block; 210 | position: absolute; 211 | top: 50%; 212 | right: 30px; 213 | border-top: 30px solid transparent; 214 | border-bottom: 30px solid transparent; 215 | border-left: 30px solid lightgray; 216 | transition: .5s ease-in-out; 217 | 218 | &:hover { 219 | border-left-color: white; 220 | } 221 | } 222 | 223 | .pxprev{ 224 | display: block; 225 | position: absolute; 226 | top: 50%; 227 | left: 30px; 228 | border-top: 30px solid transparent; 229 | border-bottom: 30px solid transparent; 230 | border-right:30px solid lightgray; 231 | 232 | transition: .5s ease-in-out; 233 | 234 | &:hover { 235 | border-right-color: white; 236 | } 237 | } 238 | } 239 | 240 | .hidden { 241 | display: none; 242 | } 243 | 244 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | ![](https://api.travis-ci.org/StevenYuysy/PXGallery.svg) 4 | 5 | PXGallery is a library which can set different layouts in pure javascript without any dependencies. 6 | 7 | # Demo 8 | 9 | [Demo](http://stevenyuysy.github.io/PXGallery/#!/setting) 10 | 11 | # Setup 12 | 13 | ## Easy setup 14 | Commonly, you can just install it with a css and javascript: 15 | 16 | ```html 17 | 18 | ``` 19 | 20 | Include the Gallery script at the bottom of the body of your webpage: 21 | 22 | ```html 23 | 187 | 188 | 270 | 271 | 272 | -------------------------------------------------------------------------------- /pxgallery.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * pxgallery v0.0.2 4 | * use with pxgallery.css 5 | * @author StevenYu 6 | */ 7 | 8 | ;(function (root, factory) { 9 | 10 | if (typeof define === 'function' && define.amd) { 11 | define([], factory); 12 | } else if (typeof module === 'object' && module.exports) { 13 | module.exports = factory(); 14 | } else { 15 | // Browser globals (root is window) 16 | root.pxgallery = factory(); 17 | } 18 | 19 | }(this, function() { 20 | 21 | 'use strict'; 22 | 23 | /** 24 | * @param {Object} opts - options used in plugin 25 | * @constructor 26 | */ 27 | 28 | var pxgallery = function(opts) { 29 | 30 | opts = opts || {}; 31 | var fullscreenSelector = opts.fullscreenSelector || '.pxfullscreen'; 32 | 33 | this.containerSelector = opts.containerSelector || '.pxgalleryContainer'; 34 | this.boxSelector = opts.boxSelector || '.pxgalleryBox'; 35 | this.container = document.querySelector(this.containerSelector); 36 | this.fullscreen = document.querySelector(fullscreenSelector); 37 | 38 | this.layout = { 39 | NONE: 0, // no layout 40 | PUZZLE: 1, // puzzle layout 41 | WATERFALL: 2, // waterfall layout 42 | BARREL: 3, // barrel layout 43 | SQUARE: 4 // square layout 44 | }; 45 | 46 | }; 47 | 48 | /** 49 | * @private 50 | */ 51 | 52 | var _options = { 53 | layout : '', 54 | puzzleHeight:'', 55 | coulumn : '', 56 | heightMin : '', 57 | gutter : '', 58 | mdSquareSize:'', 59 | smSquareSize:'', 60 | fullscreenState : '', 61 | images: [] 62 | }; 63 | 64 | // create a fullscreen for images with next and prev 65 | function _createFullscreen(event) { 66 | 67 | if (event.target.getAttribute('src').trim()) { 68 | var fullscreen = document.createElement('div'); 69 | var img = document.createElement('img'); 70 | var next = document.createElement('span'); 71 | var prev = document.createElement('span'); 72 | var currentImg = event.target.getAttribute('src'); 73 | var currentImgIndex = _options.images.indexOf(currentImg); 74 | fullscreen.className = 'pxfullscreen'; 75 | next.className = 'pxnext'; 76 | prev.className = 'pxprev'; 77 | img.src = currentImg; 78 | fullscreen.addEventListener('click', function(event) { 79 | if (event.target != next && event.target != prev) { 80 | this.remove(); 81 | } 82 | }, false); 83 | next.addEventListener('click', function() { 84 | currentImgIndex++; 85 | if (currentImgIndex >= _options.images.length) currentImgIndex = 0; 86 | img.src = _options.images[currentImgIndex]; 87 | }, false); 88 | prev.addEventListener('click', function() { 89 | currentImgIndex--; 90 | if (currentImgIndex <= 0) currentImgIndex = (_options.images.length - 1); 91 | img.src = _options.images[currentImgIndex]; 92 | }) 93 | 94 | fullscreen.appendChild(prev); 95 | fullscreen.appendChild(next); 96 | fullscreen.appendChild(img); 97 | this.parentNode.appendChild(fullscreen); 98 | } 99 | }; 100 | 101 | // add the box into column or row 102 | function _addBox(ele, index) { 103 | 104 | switch (_options.layout) { 105 | case 2: 106 | this.columns ? this.columns[index].appendChild(ele) : this.container.appendChild(ele); 107 | break; 108 | case 3: 109 | this.rows ? this.rows[index].appendChild(ele) : this.container.appendChild(ele); 110 | break; 111 | default: 112 | this.container.appendChild(ele); 113 | this.setLayout(_options.layout); 114 | } 115 | }; 116 | 117 | 118 | /** 119 | * init the album 120 | * It will replace the photos 121 | * @param {(Stirng | String[])} image - the URL of the photo or the URL array of the photos 122 | * @param {Object} opts - layout options 123 | */ 124 | 125 | pxgallery.prototype.setImage = function(image, opts) { 126 | 127 | if (typeof image === 'string') { 128 | this.setImage([image]); 129 | return; 130 | } 131 | 132 | _options.layout = opts.layout || 2; 133 | _options.puzzleHeight = opts.puzzleHeight || 400; 134 | _options.fullscreenState = opts.fullscreenState || true; 135 | _options.column = opts.column || 5; 136 | _options.heightMin = opts.heightMin || 150; 137 | _options.mdSquareSize = opts.mdSquareSize || 3; 138 | _options.smSquareSize = opts.smSquareSize || 6; 139 | _options.gutter = opts.gutter || 10; 140 | var _this = this; 141 | 142 | this.addImage(image, true); 143 | this.setLayout(_options.layout); 144 | _options.fullscreenState ? this.enableFullscreen() : this.disableFullscreen(); 145 | 146 | window.onload = function() { 147 | _this.setLayout(_options.layout); 148 | } 149 | 150 | window.onresize = function() { 151 | _this.setLayout(_options.layout); 152 | } 153 | }; 154 | 155 | /** 156 | * get the DOM elements which contain the images 157 | * @return {HTMLelement[]} boxes 158 | */ 159 | 160 | pxgallery.prototype.getImageDomElements = function() { 161 | 162 | var boxes = Array.prototype.slice.call(this.container.querySelectorAll(this.boxSelector)); 163 | var url = []; 164 | boxes.forEach(function(ele) { 165 | ele.ratio = ele.clientWidth / ele.clientHeight; 166 | url.push(ele.querySelector('img').src); 167 | }, this); 168 | _options.images = url; 169 | return boxes; 170 | }; 171 | 172 | /** 173 | * add images to the container 174 | * @param {(String | String[])} image - the URL of the photo or the URL array of the photos 175 | * @param {Boolean} raw - weather add the images to the container directly or not 176 | */ 177 | 178 | pxgallery.prototype.addImage = function(image, raw) { 179 | 180 | if (typeof image === 'string') { 181 | this.addImage([image]); 182 | return; 183 | } 184 | if (!raw) { 185 | if (_options.layout === 2) var index = this.getWaterfallHeightMin(); 186 | if (_options.layout === 3) var index = (this.rows.length - 1); 187 | } 188 | 189 | for (var i = 0; i < image.length; i++) { 190 | var div = document.createElement('div'); 191 | var img = new Image(); 192 | div.className = 'pxgalleryBox'; 193 | div.style.border = _options.gutter / 2 + 'px solid transparent'; 194 | img.src = image[i]; 195 | _options.images.push(image[i]); 196 | div.appendChild(img); 197 | if (!raw) { 198 | (_options.layout === 2 || _options.layout === 3 ) ? _addBox.call(this, div, index) : _addBox.apply(this, [div]); 199 | } 200 | else { 201 | _addBox.apply(this, [div]) 202 | } 203 | } 204 | }; 205 | 206 | /** 207 | * remove the images 208 | * @param {(HTMLelement|HTMLelement[])} image - the images that need to be removed 209 | */ 210 | 211 | pxgallery.prototype.removeImage = function(image) { 212 | 213 | image.forEach(function(ele) { 214 | var removeUrl = ele.querySelector('img').src; 215 | _options.images = _options.images.filter(function (url) { 216 | return url != removeUrl; 217 | }) 218 | ele.remove(); 219 | }, this) 220 | 221 | }; 222 | 223 | /** 224 | * set the layout 225 | * @param {Number} - layoutValue 226 | */ 227 | 228 | pxgallery.prototype.setLayout = function(layoutValue) { 229 | 230 | var boxes = this.getImageDomElements(); 231 | this.clearLayout(); 232 | _options.layout = layoutValue; 233 | 234 | switch (layoutValue) { 235 | 236 | case 0: 237 | break; 238 | 239 | case 1: 240 | if (boxes.length > 6) { 241 | console.error('PUZZLE layout only can contain 6 photos'); 242 | break; 243 | } 244 | this.container.style.height = _options.puzzleHeight + 'px'; 245 | this.container.className = this.containerSelector.slice(1) + ' puzzle-' + boxes.length; 246 | this.setPuzzleSquare(boxes.length); 247 | break; 248 | 249 | case 2: 250 | this.container.className = this.containerSelector.slice(1) + ' waterfall'; 251 | this.initWaterfallColumn(_options.column); 252 | for (var i = 0; i < boxes.length; i++) { 253 | _addBox.call(this, boxes[i], this.getWaterfallHeightMin()); 254 | } 255 | break; 256 | 257 | case 3: 258 | this.container.className = this.containerSelector.slice(1) + ' barrel'; 259 | var rows = this.setBarrelBin(); 260 | this.initBarrelBin(rows); 261 | var index = 0; 262 | for (var i = 0; i < boxes.length; i++) { 263 | if (i > rows[index].number) index ++; 264 | boxes[i].style.height = '100%'; 265 | boxes[i].style.width = ''; 266 | _addBox.call(this, boxes[i], index); 267 | } 268 | break; 269 | 270 | case 4: 271 | this.container.className = this.containerSelector.slice(1) + ' square'; 272 | for (var i = 0; i < boxes.length; i++) { 273 | var md = 'col-md-' + _options.mdSquareSize; 274 | var sm = 'col-sm-' + _options.smSquareSize; 275 | boxes[i].className = this.boxSelector.slice(1) + ' ' + md + ' ' + sm; 276 | } 277 | } 278 | }; 279 | 280 | /** 281 | * get current layout 282 | * @return {Number} -_options.layout 283 | */ 284 | 285 | pxgallery.prototype.getLayout = function() { 286 | 287 | return _options.layout; 288 | }; 289 | 290 | pxgallery.prototype.clearLayout = function() { 291 | 292 | var boxes = this.getImageDomElements(); 293 | var _this = this; 294 | this.container.className = this.containerSelector.slice(1); 295 | this.container.style.height = ''; 296 | boxes.forEach(function(ele) { 297 | ele.style.width = ''; 298 | ele.style.height = ''; 299 | }, this) 300 | 301 | if (this.columns) { 302 | this.columns.forEach(function(ele) { 303 | ele.remove(); 304 | }, this) 305 | boxes.forEach(function(ele) { 306 | _this.container.appendChild(ele); 307 | }, this) 308 | } 309 | 310 | if (this.rows) { 311 | this.rows.forEach(function(ele) { 312 | ele.remove(); 313 | }, this) 314 | boxes.forEach(function(ele) { 315 | _this.container.appendChild(ele); 316 | }, this) 317 | } 318 | _options.layout = 0; 319 | }; 320 | 321 | /** 322 | * NOT RECOMMAND, it may effect the perfomance of the browser cause it change the DOM directly 323 | * set the gutter between images 324 | * @param {Number} gutter 325 | */ 326 | 327 | pxgallery.prototype.setGutter = function(gutter) { 328 | 329 | _options.gutter = gutter; 330 | var boxes = this.getImageDomElements() 331 | boxes.forEach(function(ele) { 332 | ele.style.border = (gutter / 2) +'px solid transparent'; 333 | }, this) 334 | }; 335 | 336 | /** 337 | * enable fullscreen 338 | */ 339 | 340 | pxgallery.prototype.enableFullscreen = function() { 341 | 342 | _options.fullscreenState = true; 343 | this.container.addEventListener('click', _createFullscreen, false); 344 | }; 345 | 346 | /** 347 | * disable fullscreen 348 | */ 349 | 350 | pxgallery.prototype.disableFullscreen = function() { 351 | 352 | _options.fullscreenState = false; 353 | this.container.removeEventListener('click', _createFullscreen, false); 354 | }; 355 | 356 | /** 357 | * @return {Boolean} weather eenable fullscreen view 358 | */ 359 | 360 | pxgallery.prototype.isFullscreenEnabled = function() { 361 | 362 | return _options.fullscreenState; 363 | }; 364 | 365 | /** 366 | * set the 2nd image square 367 | * @param {Number} length -the number of the images 368 | */ 369 | 370 | pxgallery.prototype.setPuzzleSquare = function(length) { 371 | 372 | var boxes = this.getImageDomElements(); 373 | if (length === 3) { 374 | var sideLength = parseFloat(this.container.clientHeight) / 2; 375 | boxes[0].style.width = (this.container.clientWidth - sideLength) + 'px'; 376 | boxes[1].style.height = sideLength + 'px'; 377 | boxes[1].style.width = sideLength + 'px'; 378 | boxes[2].style.height = sideLength + 'px'; 379 | boxes[2].style.width = sideLength + 'px'; 380 | } else if (length === 5) { 381 | var sideLength = parseFloat(this.container.clientWidth / 3); 382 | if (parseInt(sideLength + (_options.gutter * 2)) < parseInt(_options.puzzleHeight)) { 383 | boxes[0].style.width = parseFloat(this.container.clientWidth - sideLength) + 'px'; 384 | boxes[1].style.width = sideLength + 'px'; 385 | boxes[1].style.height = sideLength + 'px'; 386 | boxes[2].style.width = sideLength + 'px'; 387 | boxes[2].style.height = parseFloat(this.container.clientHeight - sideLength) + 'px'; 388 | } else { 389 | boxes.forEach(function(ele) { 390 | ele.style.width = ''; 391 | ele.style.height= ''; 392 | }, this); 393 | } 394 | } else { 395 | return; 396 | } 397 | }; 398 | 399 | /** 400 | * NOT RECOMMAND, it may effect the perfomance of the browser cause it change the DOM directly 401 | * set the height of the container 402 | * @param {Number} - height 403 | */ 404 | 405 | pxgallery.prototype.setPuzzleHeight = function(height) { 406 | _options.puzzleHeight = height; 407 | if (_options.layout === 1) this.setLayout(_options.layout); 408 | }; 409 | 410 | /** 411 | * NOT RECOMMAND, it may effect the perfomance of the browser cause it change the DOM directly 412 | * set the number of the column 413 | * @param {NUmber} - columnNum 414 | */ 415 | 416 | pxgallery.prototype.setColumnNum = function(columnNum) { 417 | _options.column = columnNum; 418 | if (_options.layout === 2) this.setLayout(2); 419 | }; 420 | 421 | /** 422 | * init the waterfall column only after clear the columns inside the container 423 | * @param {NUmber} - columnNum 424 | */ 425 | 426 | pxgallery.prototype.initWaterfallColumn = function(columnNum) { 427 | // create column div 428 | this.columns = []; 429 | for (var i = 0; i < columnNum; i++) { 430 | var columnDiv = document.createElement('div'); 431 | columnDiv.style.width = (100/columnNum) + '%'; 432 | columnDiv.setAttribute('class','waterfallColumn'); 433 | this.columns.push(columnDiv); 434 | this.container.appendChild(columnDiv); 435 | } 436 | }; 437 | 438 | /** 439 | * get minimun height index of the column 440 | * @return {Number} - index 441 | */ 442 | 443 | pxgallery.prototype.getWaterfallHeightMin = function() { 444 | 445 | var min = this.columns[0].clientHeight; 446 | var index = 0; 447 | for (var i = 0; i < this.columns.length; i++) { 448 | if (this.columns[i].clientHeight < min) { 449 | min = this.columns[i].clientHeight; 450 | index = i; 451 | } 452 | } 453 | return index; 454 | }; 455 | 456 | /** 457 | * initaialize the barrel layout 458 | * @param {Object} - row, a row object which contain height 459 | */ 460 | 461 | pxgallery.prototype.initBarrelBin = function(row) { 462 | 463 | this.rows = []; 464 | for (var i = 0; i < row.length; i++) { 465 | var rowDiv = document.createElement('div'); 466 | rowDiv.className = 'barrelRow'; 467 | rowDiv.style.height = row[i].height + 'px'; 468 | this.rows.push(rowDiv); 469 | this.container.appendChild(rowDiv); 470 | } 471 | }; 472 | 473 | /** 474 | * calculate the ratio 475 | * @return {Object} rows 476 | */ 477 | 478 | pxgallery.prototype.setBarrelBin = function() { 479 | 480 | var boxes = this.getImageDomElements(); 481 | var height = _options.heightMin; 482 | var rows = []; 483 | var width = 0; 484 | var count = 0; 485 | var ratio; 486 | var totalWidth; 487 | var totalHeight; 488 | var restWidth; 489 | var i; 490 | 491 | // compare the total width with the container width 492 | // if the total width is grater than container width 493 | // than push to the row array which include the number and height 494 | // clear data and loop again until end 495 | 496 | for (i = 0; i < boxes.length; i++) { 497 | boxes[i].style.height = height + 'px'; 498 | boxes[i].style.width = (height * boxes[i].ratio) + 'px'; 499 | width += height * boxes[i].ratio; 500 | if (width > this.container.clientWidth) { 501 | totalWidth = width - boxes[i].clientWidth; 502 | ratio = height / totalWidth; 503 | totalHeight = this.container.clientWidth * ratio; 504 | rows.push({number: i-1, height: totalHeight}); 505 | width = boxes[i].clientWidth; 506 | } 507 | } 508 | rows.push({number: i, height: _options.heightMin}); 509 | 510 | return rows; 511 | 512 | }; 513 | 514 | /** 515 | * NOT RECOMMAND, it may effect the perfomance of the browser cause it change the DOM directly 516 | * set the height of the barrel 517 | * @param {Number} min 518 | */ 519 | 520 | pxgallery.prototype.setBarrelHeight = function(min) { 521 | 522 | _options.heightMin = min; 523 | if (_options.layout === 3) this.setLayout(3); 524 | }; 525 | 526 | /** 527 | * NOT RECOMMAND, it may effect the perfomance of the browser cause it change the DOM directly 528 | * Set the suqare size when over 768 px 529 | * @param {Number} size - (1-12) 530 | */ 531 | 532 | pxgallery.prototype.setMdSquareSize = function(size) { 533 | 534 | _options.mdSquareSize = size; 535 | if (_options.layout === 4) this.setLayout(_options.layout) 536 | }; 537 | 538 | /** 539 | * NOT RECOMMAND, it may effect the perfomance of the browser cause it change the DOM directly 540 | * Set the suqare size when under 768 px 541 | * @param {Number} size - (1-12) 542 | */ 543 | 544 | pxgallery.prototype.setSmSquareSize = function(size) { 545 | 546 | _options.smSquareSize = size; 547 | if (_options.layout === 4) this.setLayout(_options.layout) 548 | }; 549 | 550 | return pxgallery; 551 | })); 552 | -------------------------------------------------------------------------------- /demo/src/lib/pxgallery.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * pxgallery v0.0.1 4 | * use with pxgallery.css 5 | * @author StevenYu 6 | */ 7 | 8 | ;(function (root, factory) { 9 | 10 | if (typeof define === 'function' && define.amd) { 11 | define([], factory); 12 | } else if (typeof module === 'object' && module.exports) { 13 | module.exports = factory(); 14 | } else { 15 | // Browser globals (root is window) 16 | root.pxgallery = factory(); 17 | } 18 | 19 | }(this, function() { 20 | 21 | 'use strict'; 22 | 23 | /** 24 | * @param {Object} opts - options used in plugin 25 | * @constructor 26 | */ 27 | 28 | var pxgallery = function(opts) { 29 | 30 | 31 | opts = opts || {}; 32 | var fullscreenSelector = opts.fullscreenSelector || '.pxfullscreen'; 33 | 34 | this.containerSelector = opts.containerSelector || '.pxgalleryContainer'; 35 | this.boxSelector = opts.boxSelector || '.pxgalleryBox'; 36 | this.container = document.querySelector(this.containerSelector); 37 | this.fullscreen = document.querySelector(fullscreenSelector); 38 | 39 | this.layout = { 40 | NONE: 0, // no layout 41 | PUZZLE: 1, // puzzle layout 42 | WATERFALL: 2, // waterfall layout 43 | BARREL: 3, // barrel layout 44 | SQUARE: 4 // square layout 45 | }; 46 | 47 | }; 48 | 49 | /** 50 | * @private 51 | */ 52 | 53 | var _options = { 54 | layout : '', 55 | puzzleHeight:'', 56 | coulumn : '', 57 | heightMin : '', 58 | gutter : '', 59 | mdSquareSize:'', 60 | smSquareSize:'', 61 | fullscreenState : '', 62 | images: [] 63 | } 64 | 65 | // create a fullscreen for images with next and prev 66 | var _createFullscreen = function(event) { 67 | if (event.target.getAttribute('src').trim()) { 68 | var fullscreen = document.createElement('div'); 69 | var img = document.createElement('img'); 70 | var next = document.createElement('span'); 71 | var prev = document.createElement('span'); 72 | var currentImg = event.target.getAttribute('src'); 73 | var currentImgIndex = _options.images.indexOf(currentImg); 74 | fullscreen.className = 'pxfullscreen'; 75 | next.className = 'pxnext'; 76 | prev.className = 'pxprev'; 77 | img.src = currentImg; 78 | fullscreen.addEventListener('click', function(event) { 79 | if (event.target != next && event.target != prev) { 80 | this.remove(); 81 | } 82 | }, false); 83 | next.addEventListener('click', function() { 84 | currentImgIndex++; 85 | if (currentImgIndex >= _options.images.length) currentImgIndex = 0; 86 | img.src = _options.images[currentImgIndex]; 87 | }, false); 88 | prev.addEventListener('click', function() { 89 | currentImgIndex--; 90 | if (currentImgIndex <= 0) currentImgIndex = (_options.images.length - 1); 91 | img.src = _options.images[currentImgIndex]; 92 | }) 93 | 94 | fullscreen.appendChild(prev); 95 | fullscreen.appendChild(next); 96 | fullscreen.appendChild(img); 97 | this.parentNode.appendChild(fullscreen); 98 | } 99 | }; 100 | 101 | // add the box into column or row 102 | var _addBox = function(ele, index) { 103 | switch (_options.layout) { 104 | case 2: 105 | this.columns ? this.columns[index].appendChild(ele) : this.container.appendChild(ele); 106 | break; 107 | case 3: 108 | this.rows ? this.rows[index].appendChild(ele) : this.container.appendChild(ele); 109 | break; 110 | default: 111 | this.container.appendChild(ele); 112 | this.setLayout(_options.layout); 113 | } 114 | } 115 | 116 | 117 | /** 118 | * init the album 119 | * It will replace the photos 120 | * @param {(Stirng | String[])} image - the URL of the photo or the URL array of the photos 121 | * @param {Object} opts - layout options 122 | */ 123 | 124 | pxgallery.prototype.setImage = function(image, opts) { 125 | 126 | if (typeof image === 'string') { 127 | this.setImage([image]); 128 | return; 129 | } 130 | 131 | _options.layout = opts.layout || 2; 132 | _options.puzzleHeight = opts.puzzleHeight || 400; 133 | _options.fullscreenState = opts.fullscreenState || true; 134 | _options.column = opts.column || 5; 135 | _options.heightMin = opts.heightMin || 150; 136 | _options.mdSquareSize = opts.mdSquareSize || 3; 137 | _options.smSquareSize = opts.smSquareSize || 6; 138 | _options.gutter = opts.gutter || 10; 139 | var _this = this; 140 | 141 | this.addImage(image, true); 142 | this.setLayout(_options.layout); 143 | _options.fullscreenState ? this.enableFullscreen() : this.disableFullscreen(); 144 | 145 | window.onload = function() { 146 | _this.setLayout(_options.layout); 147 | } 148 | 149 | window.onresize = function() { 150 | _this.setLayout(_options.layout); 151 | } 152 | }; 153 | 154 | /** 155 | * get the DOM elements which contain the images 156 | * @return {HTMLelement[]} boxes 157 | */ 158 | 159 | pxgallery.prototype.getImageDomElements = function() { 160 | 161 | var boxes = Array.prototype.slice.call(this.container.querySelectorAll(this.boxSelector)); 162 | var url = []; 163 | boxes.forEach(function(ele) { 164 | ele.ratio = ele.clientWidth / ele.clientHeight; 165 | url.push(ele.querySelector('img').src); 166 | }, this); 167 | _options.images = url; 168 | return boxes; 169 | }; 170 | 171 | /** 172 | * add images to the container 173 | * @param {(String | String[])} image - the URL of the photo or the URL array of the photos 174 | * @param {Boolean} raw - weather add the images to the container directly or not 175 | */ 176 | 177 | pxgallery.prototype.addImage = function(image, raw) { 178 | 179 | if (typeof image === 'string') { 180 | this.addImage([image]); 181 | return; 182 | } 183 | if (!raw) { 184 | if (_options.layout === 2) var index = this.getWaterfallHeightMin(); 185 | if (_options.layout === 3) var index = (this.rows.length - 1); 186 | } 187 | 188 | for (var i = 0; i < image.length; i++) { 189 | var div = document.createElement('div'); 190 | var img = new Image(); 191 | div.className = 'pxgalleryBox'; 192 | div.style.border = _options.gutter / 2 + 'px solid transparent'; 193 | img.src = image[i]; 194 | _options.images.push(image[i]); 195 | div.appendChild(img); 196 | if (!raw) { 197 | (_options.layout === 2 || _options.layout === 3 ) ? _addBox.call(this, div, index) : _addBox.apply(this, [div]); 198 | } 199 | else { 200 | _addBox.apply(this, [div]) 201 | } 202 | } 203 | }; 204 | 205 | /** 206 | * remove the images 207 | * @param {(HTMLelement|HTMLelement[])} image - the images that need to be removed 208 | */ 209 | 210 | pxgallery.prototype.removeImage = function(image) { 211 | 212 | image.forEach(function(ele) { 213 | var removeUrl = ele.querySelector('img').src; 214 | _options.images = _options.images.filter(function (url) { 215 | return url != removeUrl; 216 | }) 217 | ele.remove(); 218 | }, this) 219 | 220 | }; 221 | 222 | /** 223 | * set the layout 224 | * @param {Number} - layoutValue 225 | */ 226 | 227 | pxgallery.prototype.setLayout = function(layoutValue) { 228 | 229 | var boxes = this.getImageDomElements(); 230 | this.clearLayout(); 231 | _options.layout = layoutValue; 232 | 233 | switch (layoutValue) { 234 | 235 | case 0: 236 | break; 237 | 238 | case 1: 239 | if (boxes.length > 6) { 240 | console.error('PUZZLE layout only can contain 6 photos'); 241 | break; 242 | } 243 | this.container.style.height = _options.puzzleHeight + 'px'; 244 | this.container.className = this.containerSelector.slice(1) + ' puzzle-' + boxes.length; 245 | this.setPuzzleSquare(boxes.length); 246 | break; 247 | 248 | case 2: 249 | this.container.className = this.containerSelector.slice(1) + ' waterfall'; 250 | this.initWaterfallColumn(_options.column); 251 | for (var i = 0; i < boxes.length; i++) { 252 | _addBox.call(this, boxes[i], this.getWaterfallHeightMin()); 253 | } 254 | break; 255 | 256 | case 3: 257 | this.container.className = this.containerSelector.slice(1) + ' barrel'; 258 | var rows = this.setBarrelBin(); 259 | this.initBarrelBin(rows); 260 | var index = 0; 261 | for (var i = 0; i < boxes.length; i++) { 262 | if (i > rows[index].number) index ++; 263 | boxes[i].style.height = '100%'; 264 | boxes[i].style.width = ''; 265 | _addBox.call(this, boxes[i], index); 266 | } 267 | break; 268 | 269 | case 4: 270 | this.container.className = this.containerSelector.slice(1) + ' square'; 271 | for (var i = 0; i < boxes.length; i++) { 272 | var md = 'col-md-' + _options.mdSquareSize; 273 | var sm = 'col-sm-' + _options.smSquareSize; 274 | boxes[i].className = this.boxSelector.slice(1) + ' ' + md + ' ' + sm; 275 | } 276 | } 277 | }; 278 | 279 | /** 280 | * get current layout 281 | * @return {Number} -_options.layout 282 | */ 283 | 284 | pxgallery.prototype.getLayout = function() { 285 | return _options.layout; 286 | }; 287 | 288 | pxgallery.prototype.clearLayout = function() { 289 | 290 | var boxes = this.getImageDomElements(); 291 | var _this = this; 292 | this.container.className = this.containerSelector.slice(1); 293 | this.container.style.height = ''; 294 | boxes.forEach(function(ele) { 295 | ele.style.width = ''; 296 | ele.style.height = ''; 297 | }, this) 298 | 299 | if (this.columns) { 300 | this.columns.forEach(function(ele) { 301 | ele.remove(); 302 | }, this) 303 | boxes.forEach(function(ele) { 304 | _this.container.appendChild(ele); 305 | }, this) 306 | } 307 | 308 | if (this.rows) { 309 | this.rows.forEach(function(ele) { 310 | ele.remove(); 311 | }, this) 312 | boxes.forEach(function(ele) { 313 | _this.container.appendChild(ele); 314 | }, this) 315 | } 316 | _options.layout = 0; 317 | }; 318 | 319 | /** 320 | * NOT RECOMMAND, it may effect the perfomance of the browser cause it change the DOM directly 321 | * set the gutter between images 322 | * @param {Number} - gutter 323 | */ 324 | 325 | pxgallery.prototype.setGutter = function(gutter) { 326 | 327 | _options.gutter = gutter; 328 | var boxes = this.getImageDomElements() 329 | boxes.forEach(function(ele) { 330 | ele.style.border = (gutter / 2) +'px solid transparent'; 331 | }, this) 332 | }; 333 | 334 | /** 335 | * enable fullscreen 336 | */ 337 | 338 | pxgallery.prototype.enableFullscreen = function() { 339 | 340 | _options.fullscreenState = true; 341 | this.container.addEventListener('click', _createFullscreen, false); 342 | }; 343 | 344 | /** 345 | * disable fullscreen 346 | */ 347 | 348 | pxgallery.prototype.disableFullscreen = function() { 349 | 350 | _options.fullscreenState = false; 351 | this.container.removeEventListener('click', _createFullscreen, false); 352 | }; 353 | 354 | /** 355 | * @return {Boolean} weather eenable fullscreen view 356 | */ 357 | 358 | pxgallery.prototype.isFullscreenEnabled = function() { 359 | 360 | return _options.fullscreenState; 361 | }; 362 | 363 | /** 364 | * set the 2nd image square 365 | * @param {Number} length -the number of the images 366 | */ 367 | 368 | pxgallery.prototype.setPuzzleSquare = function(length) { 369 | 370 | var boxes = this.getImageDomElements(); 371 | if (length === 3) { 372 | var sideLength = parseFloat(this.container.clientHeight) / 2; 373 | boxes[0].style.width = (this.container.clientWidth - sideLength) + 'px'; 374 | boxes[1].style.height = sideLength + 'px'; 375 | boxes[1].style.width = sideLength + 'px'; 376 | boxes[2].style.height = sideLength + 'px'; 377 | boxes[2].style.width = sideLength + 'px'; 378 | } else if (length === 5) { 379 | var sideLength = parseFloat(this.container.clientWidth / 3); 380 | if (parseInt(sideLength + (_options.gutter * 2)) < parseInt(_options.puzzleHeight)) { 381 | boxes[0].style.width = parseFloat(this.container.clientWidth - sideLength) + 'px'; 382 | boxes[1].style.width = sideLength + 'px'; 383 | boxes[1].style.height = sideLength + 'px'; 384 | boxes[2].style.width = sideLength + 'px'; 385 | boxes[2].style.height = parseFloat(this.container.clientHeight - sideLength) + 'px'; 386 | } else { 387 | boxes.forEach(function(ele) { 388 | ele.style.width = ''; 389 | ele.style.height= ''; 390 | }, this); 391 | } 392 | } else { 393 | return; 394 | } 395 | }; 396 | 397 | /** 398 | * NOT RECOMMAND, it may effect the perfomance of the browser cause it change the DOM directly 399 | * set the height of the container 400 | * @param {Number} - height 401 | */ 402 | 403 | pxgallery.prototype.setPuzzleHeight = function(height) { 404 | _options.puzzleHeight = height; 405 | if (_options.layout === 1) this.setLayout(_options.layout); 406 | }; 407 | 408 | /** 409 | * NOT RECOMMAND, it may effect the perfomance of the browser cause it change the DOM directly 410 | * set the number of the column 411 | * @param {NUmber} - columnNum 412 | */ 413 | 414 | pxgallery.prototype.setColumnNum = function(columnNum) { 415 | _options.column = columnNum; 416 | if (_options.layout === 2) this.setLayout(2); 417 | }; 418 | 419 | /** 420 | * init the waterfall column only after clear the columns inside the container 421 | * @param {NUmber} - columnNum 422 | */ 423 | 424 | pxgallery.prototype.initWaterfallColumn = function(columnNum) { 425 | // create column div 426 | this.columns = []; 427 | for (var i = 0; i < columnNum; i++) { 428 | var columnDiv = document.createElement('div'); 429 | columnDiv.style.width = (100/columnNum) + '%'; 430 | columnDiv.setAttribute('class','waterfallColumn'); 431 | this.columns.push(columnDiv); 432 | this.container.appendChild(columnDiv); 433 | } 434 | }; 435 | 436 | /** 437 | * get minimun height index of the column 438 | * @return {Number} - index 439 | */ 440 | 441 | pxgallery.prototype.getWaterfallHeightMin = function() { 442 | 443 | var min = this.columns[0].clientHeight; 444 | var index = 0; 445 | for (var i = 0; i < this.columns.length; i++) { 446 | if (this.columns[i].clientHeight < min) { 447 | min = this.columns[i].clientHeight; 448 | index = i; 449 | } 450 | } 451 | return index; 452 | }; 453 | 454 | /** 455 | * initaialize the barrel layout 456 | * @param {Object} - row, a row object which contain height 457 | */ 458 | 459 | pxgallery.prototype.initBarrelBin = function(row) { 460 | 461 | this.rows = []; 462 | for (var i = 0; i < row.length; i++) { 463 | var rowDiv = document.createElement('div'); 464 | rowDiv.className = 'barrelRow'; 465 | rowDiv.style.height = row[i].height + 'px'; 466 | this.rows.push(rowDiv); 467 | this.container.appendChild(rowDiv); 468 | } 469 | }; 470 | 471 | /** 472 | * calculate the ratio 473 | * @return {Object} - rows 474 | */ 475 | 476 | pxgallery.prototype.setBarrelBin = function() { 477 | 478 | var boxes = this.getImageDomElements(); 479 | var height = _options.heightMin; 480 | var rows = []; 481 | var width = 0; 482 | var count = 0; 483 | var ratio; 484 | var totalWidth; 485 | var totalHeight; 486 | var restWidth; 487 | var i; 488 | 489 | // compare the total width with the container width 490 | // if the total width is grater than container width 491 | // than push to the row array which include the number and height 492 | // clear data and loop again until end 493 | 494 | for (i = 0; i < boxes.length; i++) { 495 | boxes[i].style.height = height + 'px'; 496 | boxes[i].style.width = (height * boxes[i].ratio) + 'px'; 497 | width += height * boxes[i].ratio; 498 | if (width > this.container.clientWidth) { 499 | totalWidth = width - boxes[i].clientWidth; 500 | ratio = height / totalWidth; 501 | totalHeight = this.container.clientWidth * ratio; 502 | rows.push({number: i-1, height: totalHeight}); 503 | width = boxes[i].clientWidth; 504 | } 505 | } 506 | rows.push({number: i, height: _options.heightMin}); 507 | 508 | return rows; 509 | 510 | }; 511 | 512 | /** 513 | * NOT RECOMMAND, it may effect the perfomance of the browser cause it change the DOM directly 514 | * set the height of the barrel 515 | * @param {Number} - min 516 | */ 517 | 518 | pxgallery.prototype.setBarrelHeight = function(min) { 519 | 520 | _options.heightMin = min; 521 | if (_options.layout === 3) this.setLayout(3); 522 | }; 523 | 524 | /** 525 | * NOT RECOMMAND, it may effect the perfomance of the browser cause it change the DOM directly 526 | * Set the suqare size when over 768 px 527 | * @param {Number} size - (1-12) 528 | */ 529 | 530 | pxgallery.prototype.setMdSquareSize = function(size) { 531 | 532 | _options.mdSquareSize = size; 533 | if (_options.layout === 4) this.setLayout(_options.layout) 534 | }; 535 | 536 | /** 537 | * NOT RECOMMAND, it may effect the perfomance of the browser cause it change the DOM directly 538 | * Set the suqare size when under 768 px 539 | * @param {Number} size - (1-12) 540 | */ 541 | 542 | pxgallery.prototype.setSmSquareSize = function(size) { 543 | 544 | _options.smSquareSize = size; 545 | if (_options.layout === 4) this.setLayout(_options.layout) 546 | }; 547 | 548 | return pxgallery; 549 | })); 550 | --------------------------------------------------------------------------------