├── vue.config.js
├── demo
├── vue.config.js
├── after.jpg
├── before.jpg
├── babel.config.js
├── index.js
├── DemoApp.vue
└── styles.css
├── .gitignore
├── src
├── index.js
└── TwentyTwenty.vue
├── .github
└── workflows
│ └── npm-publish.yml
├── LICENSE
├── index.html
├── package.json
└── README.md
/vue.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | transpileDependencies: [],
3 | }
--------------------------------------------------------------------------------
/demo/vue.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | transpileDependencies: [],
3 | }
--------------------------------------------------------------------------------
/demo/after.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhayes/vue-twentytwenty/HEAD/demo/after.jpg
--------------------------------------------------------------------------------
/demo/before.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhayes/vue-twentytwenty/HEAD/demo/before.jpg
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.DS_Store
2 | /node_modules/*
3 | npm-debug.log
4 | /dist/*
5 | yarn-error.log
6 |
--------------------------------------------------------------------------------
/demo/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/cli-plugin-babel/preset'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/demo/index.js:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue'
2 | import DemoApp from './DemoApp.vue'
3 |
4 | createApp(DemoApp).mount('#app')
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import TwentyTwenty from './TwentyTwenty.vue';
3 |
4 | Vue.component('TwentyTwenty', TwentyTwenty);
5 |
6 | export default TwentyTwenty;
7 |
--------------------------------------------------------------------------------
/.github/workflows/npm-publish.yml:
--------------------------------------------------------------------------------
1 | on:
2 | push:
3 | tags:
4 | - '*'
5 |
6 | jobs:
7 | publish-tag:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/checkout@v1
11 | - uses: actions/setup-node@v1
12 | with:
13 | node-version: 16
14 | - run: yarn install
15 | - run: yarn build
16 | - uses: JS-DevTools/npm-publish@v1
17 | with:
18 | token: ${{ secrets.NPM_TOKEN }}
19 |
--------------------------------------------------------------------------------
/demo/DemoApp.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Demo
4 |
12 |
13 |
14 |
15 |
25 |
26 |
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Mark Hayes
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
19 |
20 |
21 |
27 |
28 |
tl;dr — A picture is worth a thousand words
29 |
30 |
31 |
36 |
37 |
38 |
39 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-twentytwenty",
3 | "version": "1.0.0-1",
4 | "author": "Mark Hayes ",
5 | "scripts": {
6 | "serve": "vue-cli-service serve demo/index.js",
7 | "build": "vue-cli-service build --target lib --name vue-twentytwenty ./src/index.js",
8 | "lint": "vue-cli-service lint"
9 | },
10 | "main": "./dist/vue-twentytwenty.common.js",
11 | "files": [
12 | "dist/*",
13 | "src/*",
14 | "public/*",
15 | "*.json"
16 | ],
17 | "devDependencies": {
18 | "@vue/cli-plugin-babel": "~5.0.0-rc.1",
19 | "@vue/cli-plugin-eslint": "~5.0.0-rc.1",
20 | "@vue/cli-service": "^5.0.0-rc.1",
21 | "@vue/compiler-sfc": "^3.2.24",
22 | "babel-eslint": "^10.1.0",
23 | "eslint": "^7.32.0",
24 | "eslint-plugin-vue": "^8.0.3",
25 | "vue": "^3.2.24"
26 | },
27 | "eslintConfig": {
28 | "root": true,
29 | "env": {
30 | "node": true
31 | },
32 | "extends": [
33 | "plugin:vue/essential",
34 | "eslint:recommended"
35 | ],
36 | "parserOptions": {
37 | "parser": "babel-eslint"
38 | },
39 | "rules": {}
40 | },
41 | "browserslist": [
42 | "> 1%",
43 | "last 2 versions",
44 | "not dead"
45 | ],
46 | "contributors": [
47 | "Mattia Astorino (https://equinsuocha.io)",
48 | "Michele Riva (https://micheleriva.it)",
49 | "Estee Tey (https://esteeytey.dev)"
50 | ],
51 | "license": "MIT",
52 | "repository": {
53 | "type": "git",
54 | "url": "https://github.com/mhayes/vue-twentytwenty"
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TwentyTwenty
2 |
3 | [
4 | ](https://www.npmjs.com/package/vue-twentytwenty)
5 | [](https://vuejs.org/)
6 |
7 | A small component to quickly let users see the differences between 2 images. Based on the work I did for ZURB's [TwentyTwenty plugin](http://zurb.com/playground/twentytwenty).
8 |
9 | Live Demo
10 |
11 | Looking for Vue 2 support? Use the [v2](https://github.com/mhayes/vue-twentytwenty/tree/v2) branch.
12 |
13 | ## Installation
14 |
15 | ```
16 | $ npm install vue-twentytwenty --save
17 | ```
18 |
19 | Or download the latest release using:
20 |
21 | * https://unpkg.com/vue-twentytwenty/dist/vue-twentytwenty.umd.min.js
22 | * https://unpkg.com/vue-twentytwenty/dist/vue-twentytwenty.css
23 |
24 | ## Usage
25 |
26 | `TwentyTwenty` is a UMD module, which can be used as a module in both CommonJS and AMD modular environments. When in a non-modular environment, `TwentyTwenty` will be registered as a global variable.
27 |
28 | ### ES6
29 |
30 | ```js
31 | import 'vue-twentytwenty/dist/vue-twentytwenty.css';
32 | import TwentyTwenty from 'vue-twentytwenty';
33 |
34 | export default {
35 | // ...
36 | components: {
37 | TwentyTwenty
38 | }
39 | // ...
40 | };
41 | ```
42 |
43 | It can then be used like so:
44 |
45 | ```html
46 |
49 | ```
50 |
51 | ### CommonJS
52 |
53 | ```js
54 | var Vue = require('vue')
55 | var TwentyTwenty = require('vue-twentytwenty')
56 |
57 | var YourComponent = Vue.extend({
58 | // ...
59 | components: {
60 | 'twentytwenty': TwentyTwenty
61 | },
62 | // ...
63 | })
64 | ```
65 |
66 | ### Browser
67 |
68 | ```html
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
79 |
80 |
81 |
82 |
87 |
88 |
89 | ```
90 |
91 | ## Props
92 | | Props | Description | Required | Type | Default |
93 | | ------------ | ----------------------------------------------------------------------- | -------- | ------ | ------- |
94 | | before | URL of before image | true | String | - |
95 | | beforeLabel | When hovering over image what label should show up over before image | false | String | - |
96 | | after | URL of after image | true | String | - |
97 | | afterLabel | When hovering over image what label should show up over after image | false | String | - |
98 | | offset | How far from the left the slider should be on load (between 0 and 1) | false | Number | 0.5 |
99 | | keyboardStep | How far the slider should be moved on arrow key press (between 0 and 1) | false | Number | 0.2 |
100 |
101 | ## Usage
102 |
103 | ### Simple
104 |
105 | ```vue
106 |
107 |
110 |
111 |
112 |
122 | ```
123 |
124 | ### Advanced
125 |
126 | ```vue
127 |
128 |
134 |
135 |
136 |
146 | ```
147 |
148 | ### Publish
149 |
150 | From a clean repository (no pending changes) run the following:
151 |
152 | ```
153 | yarn version --patch
154 | git push --follow-tags
155 | ```
156 |
--------------------------------------------------------------------------------
/demo/styles.css:
--------------------------------------------------------------------------------
1 | /*! normalize.css v3.0.1 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}body{background-color:transparent;color:#555;font-family:'Roboto',sans-serif;max-width:40.625em;margin:0 auto;padding:1.25em;font-weight:300;line-height:1.6;font-size:1.1em}a{text-decoration:none;color:#428bca}a:hover{color:rgba(66,139,202,0.85)}footer{text-align:center}p{white-space:normal;word-wrap:break-word}code{font-family:Consolas, "Liberation Mono", Menlo, Courier, monospace}h1,h2,h3,h4,h5,h6{line-height:1.1}.dark-links a,.header .name a,.header .menu a,.social-icons a{color:#555;font-weight:300;text-decoration:none}.dark-links a:hover,.header .name a:hover,.header .menu a:hover,.social-icons a:hover{color:rgba(85,85,85,0.8)}.dark-links a:visited,.header .name a:visited,.header .menu a:visited,.social-icons a:visited{color:#555}.inline-code code,.post blockquote code,.post li code,.post p code{background-color:rgba(0,0,0,0.04);border-radius:.18rem;color:#555;font-size:85%;padding:.2em 0;margin:0}.inline-code code:before,.post blockquote code:before,.post li code:before,.post p code:before{letter-spacing:-.2em;content:" "}.inline-code code:after,.post blockquote code:after,.post li code:after,.post p code:after{letter-spacing:-.2em;content:" "}.header{text-align:center}.header .name{display:block;text-transform:lowercase;font-size:2.4em}.header .menu{list-style:none;padding-left:0;margin-bottom:3em}.header .menu li{display:inline;margin-right:0.8em}.header .menu li:last-child{margin-right:0}.posts-list{padding-left:0;line-height:1.4;list-style:none}.posts-list li{padding:.315em;border-bottom:1px solid #ebebeb}.posts-list li:last-child{margin-bottom:2em}.posts-list li span{color:#636363;float:right;font-size:.8em;margin-left:1.4em}.social-icons{padding-left:0;list-style:none;font-size:1.6em}.social-icons li{display:inline;margin-right:0.4em}.social-icons li:last-child{margin-right:0}.active{text-decoration:underline}.active a{color:rgba(85,85,85,0.8) !important}.date{color:#ccc;text-transform:uppercase}.gravatar{margin:.3em 1.2em 1em 0;border-radius:0.3rem}.post img{display:block;background:transparent;max-width:100%;height:auto;margin:0 auto;border:.0625em solid #ebebeb;padding:0.1875em}.post blockquote{border-left:.25em solid #ebebeb;padding:0 1em;margin:1em 0;color:#999}.post .video{display:block;background:transparent;max-width:100%;height:auto;width:16em;height:9em;margin:0 auto;border:.0625em solid #ebebeb;padding:0.1875em}.center-element{display:block;text-align:center;margin:0 auto}.highlight{margin:1em 0}@media (min-width: 48em){.header .name{font-size:3em}.post .video{width:26.625em;height:15em}}@media (min-width: 64em){.header .name{font-size:3.6em}.post .video{width:40em;height:22.5em}h1{font-size:2.6em}h2{font-size:2em}h3{font-size:1.5em}h4{font-size:1.125em}h5{font-size:0.85em}h6{font-size:0.64em}}.highlight{background-color:rgba(0,0,0,0.04);border-radius:.18rem;color:#555;padding:.4em .6em;margin:1.1em 0;overflow:hidden}.highlight .c{color:#586E75}.highlight .err{color:#93A1A1}.highlight .g{color:#93A1A1}.highlight .k{color:#859900}.highlight .l{color:#93A1A1}.highlight .n{color:#93A1A1}.highlight .o{color:#859900}.highlight .x{color:#CB4B16}.highlight .p{color:#93A1A1}.highlight .cm{color:#586E75}.highlight .cp{color:#859900}.highlight .c1{color:#586E75}.highlight .cs{color:#859900}.highlight .gd{color:#2AA198}.highlight .ge{color:#93A1A1;font-style:italic}.highlight .gr{color:#DC322F}.highlight .gh{color:#CB4B16}.highlight .gi{color:#859900}.highlight .go{color:#93A1A1}.highlight .gp{color:#93A1A1}.highlight .gs{color:#93A1A1;font-weight:700}.highlight .gu{color:#CB4B16}.highlight .gt{color:#93A1A1}.highlight .kc{color:#CB4B16}.highlight .kd{color:#268BD2}.highlight .kn{color:#859900}.highlight .kp{color:#859900}.highlight .kr{color:#268BD2}.highlight .kt{color:#DC322F}.highlight .ld{color:#93A1A1}.highlight .m{color:#2AA198}.highlight .s{color:#2AA198}.highlight .na{color:#93A1A1}.highlight .nb{color:#B58900}.highlight .nc{color:#268BD2}.highlight .no{color:#CB4B16}.highlight .nd{color:#268BD2}.highlight .ni{color:#CB4B16}.highlight .ne{color:#CB4B16}.highlight .nf{color:#268BD2}.highlight .nl{color:#93A1A1}.highlight .nn{color:#93A1A1}.highlight .nx{color:#555}.highlight .py{color:#93A1A1}.highlight .nt{color:#268BD2}.highlight .nv{color:#268BD2}.highlight .ow{color:#859900}.highlight .w{color:#93A1A1}.highlight .mf{color:#2AA198}.highlight .mh{color:#2AA198}.highlight .mi{color:#2AA198}.highlight .mo{color:#2AA198}.highlight .sb{color:#586E75}.highlight .sc{color:#2AA198}.highlight .sd{color:#93A1A1}.highlight .s2{color:#2AA198}.highlight .se{color:#CB4B16}.highlight .sh{color:#93A1A1}.highlight .si{color:#2AA198}.highlight .sx{color:#2AA198}.highlight .sr{color:#DC322F}.highlight .s1{color:#2AA198}.highlight .ss{color:#2AA198}.highlight .bp{color:#268BD2}.highlight .vc{color:#268BD2}.highlight .vg{color:#268BD2}.highlight .vi{color:#268BD2}.highlight .il{color:#2AA198}.carousel{clear:both}.carousel img{display:inline-block;width:32.333%}h1{margin-bottom:0}.subheader{font-size:1.1em;font-weight:100;color:#ccc}span.draft{background:#ccc;border-radius:3px;padding:3px 5px}
2 |
--------------------------------------------------------------------------------
/src/TwentyTwenty.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
14 |
15 |
17 |
{{beforeLabel}}
18 |
{{afterLabel}}
19 |
20 |
21 |
28 |
29 |
30 |
31 |
153 |
154 |
250 |
--------------------------------------------------------------------------------