├── .browserslistrc ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .husky └── pre-commit ├── .size-snapshot.json ├── .travis.yml ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── babel.config.js ├── dev ├── serve.js └── serve.vue ├── package.json ├── renovate.json ├── rollup.config.js ├── src ├── components │ ├── VueSignaturePad.vue │ ├── __tests__ │ │ ├── VueSignaturePad.spec.js │ │ └── mock.js │ └── index.js ├── entry.esm.js ├── entry.js └── utils │ ├── __tests__ │ └── index.spec.js │ └── index.js └── yarn.lock /.browserslistrc: -------------------------------------------------------------------------------- 1 | current node 2 | last 2 versions and > 2% 3 | ie > 10 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dist 2 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parserOptions: { 3 | parser: 'babel-eslint', 4 | sourceType: 'module' 5 | }, 6 | env: { 7 | browser: true, 8 | es6: true 9 | }, 10 | extends: ['prettier', 'plugin:vue/recommended'], 11 | plugins: ['prettier', 'vue'] 12 | }; 13 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Node CI 2 | on: 3 | push: 4 | branches: [master] 5 | pull_request: 6 | branches: [master] 7 | jobs: 8 | lint-and-test: 9 | name: 'Lint & Tests' 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v2 14 | 15 | - name: Set up Node.js 16 | uses: actions/setup-node@v2 17 | with: 18 | node-version: '12' 19 | 20 | - name: Installing Deps 21 | run: | 22 | yarn install 23 | 24 | - name: Lint & Tests 25 | run: | 26 | yarn run lint 27 | yarn test 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | coverage 4 | node_modules 5 | dist 6 | 7 | npm-debug.log 8 | yarn-error.log 9 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /.size-snapshot.json: -------------------------------------------------------------------------------- 1 | { 2 | "dist/vue-signature-pad.esm.js": { 3 | "bundled": 9942, 4 | "minified": 4970, 5 | "gzipped": 1921, 6 | "treeshaked": { 7 | "rollup": { 8 | "code": 4311, 9 | "import_statements": 57 10 | }, 11 | "webpack": { 12 | "code": 5342 13 | } 14 | } 15 | }, 16 | "dist/vue-signature-pad.cjs.js": { 17 | "bundled": 6614, 18 | "minified": 4026, 19 | "gzipped": 1568 20 | }, 21 | "dist/vue-signature-pad.min.js": { 22 | "bundled": 10219, 23 | "minified": 4487, 24 | "gzipped": 1781 25 | }, 26 | "dist/vue-signature-pad.ssr.js": { 27 | "bundled": 3977, 28 | "minified": 1440, 29 | "gzipped": 682 30 | }, 31 | "vue-signature-pad.esm.js": { 32 | "bundled": 6083, 33 | "minified": 3856, 34 | "gzipped": 1543, 35 | "treeshaked": { 36 | "rollup": { 37 | "code": 3373, 38 | "import_statements": 102 39 | }, 40 | "webpack": { 41 | "code": 4462 42 | } 43 | } 44 | }, 45 | "vue-signature-pad.ssr.js": { 46 | "bundled": 11224, 47 | "minified": 7388, 48 | "gzipped": 2552 49 | }, 50 | "vue-signature-pad.min.js": { 51 | "bundled": 6191, 52 | "minified": 6186, 53 | "gzipped": 2308 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: node_js 4 | 5 | addons: 6 | apt: 7 | sources: 8 | - ubuntu-toolchain-r-test 9 | packages: 10 | - libcairo2-dev 11 | - libjpeg8-dev 12 | - libpango1.0-dev 13 | - libgif-dev 14 | - g++-4.9 15 | env: 16 | - CXX=g++-4.9 17 | 18 | node_js: 19 | - '10' 20 | 21 | cache: 22 | yarn: true 23 | directories: 24 | - node_modules 25 | 26 | script: 27 | - "npm run lint && npm run test" 28 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.codeActionsOnSave": { 3 | "source.fixAll.eslint": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Peng Jie 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vue Signature Pad 2 | 3 | [![Build Status](https://flat.badgen.net/travis/neighborhood999/vue-signature-pad)](https://travis-ci.org/neighborhood999/vue-signature-pad) 4 | [![npm](https://flat.badgen.net/npm/v/vue-signature-pad)](https://www.npmjs.com/package/vue-signature-pad) 5 | [![styled with prettier](https://flat.badgen.net/badge/style%20with/prettier/ff69b4)](https://github.com/prettier/prettier) 6 | ![](https://flat.badgen.net/badge/module%20formats/cjs,%20esm,%20umd/green) 7 | 8 | > Vue component wrap for [signature pad](https://github.com/szimek/signature_pad) 9 | 10 | > Note: If you are still using Vue 2, please install **2.0.5** version, for Vue 3 you can install the **latest publish version**. 11 | ## Demo Vue 2 12 | 13 | [![Edit Vue Signature Pad Demo](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/n5qjp3oqv4) 14 | 15 | ## Demo Vue 3 16 | 17 | [![Edit Vue Signature Pad Demo](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/broken-flower-22ot7m) 18 | 19 | 20 | 21 | ## Installation 22 | 23 | ```sh 24 | $ yarn add vue-signature-pad 25 | ``` 26 | 27 | ## Usage for Vue 2 28 | 29 | ```js 30 | import Vue from 'vue'; 31 | import VueSignaturePad from 'vue-signature-pad'; 32 | 33 | Vue.use(VueSignaturePad); 34 | ``` 35 | 36 | ```vue 37 | 46 | 61 | ``` 62 | 63 | ## Usage for Vue3 64 | 65 | ```js 66 | import { createApp } from 'vue' 67 | import App from './App.vue' 68 | import { VueSignaturePad } from 'vue-signature-pad'; 69 | 70 | const app = createApp(App) 71 | app.component("VueSignaturePad", VueSignaturePad); 72 | app.mount('#app') 73 | ``` 74 | ```vue 75 | 84 | 101 | ``` 102 | 103 | ### In Single Component 104 | 105 | ```vue 106 | 115 | 134 | ``` 135 | 136 | 137 | [vue-signature-pad](https://github.com/neighborhood999/vue-signature-pad) use [szimek/signature_pad](https://github.com/szimek/signature_pad) default setting as `options`, feel free custom you wanted options. In [v1.1](https://github.com/neighborhood999/vue-signature-pad/releases/tag/1.1.0) add `onBegin` and `onEnd` event callback: 138 | 139 | ```vue 140 | 150 | 162 | ``` 163 | 164 | ## Props 165 | 166 | | Name | Type | Default | Description | Example | 167 | |:------------------------|:--------|:--------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------| 168 | | width | String | `100%` | Set the `div` width. | - | 169 | | height | String | `100%` | Set the `div` height. | - | 170 | | options | Object | [Reference](https://github.com/neighborhood999/vue-signature-pad/blob/master/src/utils/index.js#L5-L13) | Set the signature pad options. | [Reference](https://github.com/neighborhood999/vue-signature-pad/blob/master/src/utils/index.js#L5-L13) | 171 | | images | Array | `[]` | Merge signature with the provide images. | `['A.png', 'B.png', 'C.png']` or `[{ src: 'A.png', x: 0, y: 0 }, { src: 'B.png', x: 0, y: 10 }, { src: 'C.png', x: 0, y: 20 }]` | 172 | | customStyle | Object | `{}` | Custom `div` style. | `{ border: black 3px solid }` | 173 | | scaleToDevicePixelRatio | Boolean | `true` | Scale the canvas up to match the [device pixel ratio](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio). | - | 174 | 175 | ## Methods 176 | 177 | | Name | Argument Type | Description | 178 | | :------------------------------------- | :--------------------------- | --------------------------------------------------------------------------- | 179 | | `saveSignature(type, encoderOptions)` | `(String, Number)` | Will return target canvas **status** and **data**. | 180 | | `undoSignature()` | - | Undo | 181 | | `clearSignature()` | - | Clear | 182 | | `mergeImageAndSignature(signature)` | `Object` or `String` | Provide `images` as props and will merge with signature. | 183 | | `addImages(images)` | `Array` | Provide the images merge with signature. Reference above `images` property. | 184 | | `lockSignaturePad()` | - | Lock target signature pad. | 185 | | `openSignaturePad()` | - | Open target signature pad. | 186 | | `getPropImagesAndCacheImages()` | - | Get all the images information. | 187 | | `clearCacheImages()` | - | Clear cache images. | 188 | | `fromDataURL(data, options, callback)` | `(String, Object, Callback)` | Draw image from data URL. | 189 | | `fromData(data)` | `String` | Returns signature image as an array of point groups. | 190 | | `toData()` | - | Draws signature image from an array of point groups. | 191 | | `isEmpty()` | - | Return signature canvas have data. | 192 | 193 | ## Credits 194 | 195 | [szimek/signature_pad](https://github.com/szimek/signature_pad) - HTML5 canvas based smooth signature drawing 196 | 197 | ## LICENSE 198 | 199 | MIT © [Peng Jie](https://github.com/neighborhood999/) 200 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | const devPresets = ['@vue/babel-preset-app']; 2 | const buildPresets = [ 3 | [ 4 | '@babel/preset-env', 5 | // Config for @babel/preset-env 6 | {} 7 | ] 8 | ]; 9 | 10 | module.exports = { 11 | presets: process.env.NODE_ENV === 'development' ? devPresets : buildPresets 12 | }; 13 | -------------------------------------------------------------------------------- /dev/serve.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue'; 2 | import Dev from './serve.vue'; 3 | 4 | const app = createApp(Dev); 5 | app.mount('#app'); 6 | -------------------------------------------------------------------------------- /dev/serve.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 41 | 66 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-signature-pad", 3 | "description": "SignaturePad component for Vue.js", 4 | "version": "3.0.3", 5 | "main": "dist/vue-signature-pad.ssr.js", 6 | "unpkg": "dist/vue-signature-pad.min.js", 7 | "module": "dist/vue-signature-pad.esm.js", 8 | "browser": "dist/vue-signature-pad.esm.js", 9 | "files": [ 10 | "dist", 11 | "src/**/*.vue" 12 | ], 13 | "scripts": { 14 | "serve": "vue-cli-service serve dev/serve.js", 15 | "test": "jest", 16 | "build": "npm run clean && rollup -c", 17 | "build:ssr": "rollup --config build/rollup.config.js --format cjs", 18 | "build:es": "rollup --config build/rollup.config.js --format es", 19 | "build:unpkg": "rollup --config build/rollup.config.js --format iife", 20 | "lint": "eslint --ext .vue,.js src", 21 | "lint-fix": "eslint --ext .vue,.js src --fix", 22 | "size": "run-s size:*", 23 | "size:cjs": "echo CommonJs gzipped size: $(gzip-size $npm_package_main)", 24 | "size:unpkg": "echo UMD gzipped size: $(gzip-size $npm_package_unpkg)", 25 | "size:esm": "echo ESModule gzipped size: $(gzip-size $npm_package_module)", 26 | "clean": "rimraf dist", 27 | "prepublishOnly": "npm test && npm run lint", 28 | "prepare": "husky install" 29 | }, 30 | "jest": { 31 | "moduleFileExtensions": [ 32 | "js", 33 | "vue" 34 | ], 35 | "testMatch": [ 36 | "/src/*/__tests__/*.spec.js" 37 | ], 38 | "transform": { 39 | "^.+\\.js$": "babel-jest", 40 | "^.+\\.vue$": "vue-jest" 41 | }, 42 | "moduleNameMapper": { 43 | "^@/(.*)$": "/src/$1" 44 | }, 45 | "verbose": true, 46 | "collectCoverage": true, 47 | "setupFiles": [ 48 | "jest-canvas-mock" 49 | ] 50 | }, 51 | "lint-staged": { 52 | "*.{js,vue}": [ 53 | "eslint --fix" 54 | ] 55 | }, 56 | "author": "Peng Jie (https://github.com/neighborhood999)", 57 | "repository": { 58 | "type": "git", 59 | "url": "git+https://github.com/neighborhood999/vue-signature-pad.git" 60 | }, 61 | "keywords": [ 62 | "vue", 63 | "signature", 64 | "component" 65 | ], 66 | "bugs": { 67 | "url": "https://github.com/neighborhood999/vue-signature-pad/issues" 68 | }, 69 | "dependencies": { 70 | "merge-images": "^1.1.0", 71 | "signature_pad": "^3.0.0-beta.3" 72 | }, 73 | "devDependencies": { 74 | "@babel/core": "7.15.0", 75 | "@babel/preset-env": "7.15.0", 76 | "@rollup/plugin-alias": "3.1.5", 77 | "@rollup/plugin-babel": "5.3.0", 78 | "@rollup/plugin-commonjs": "19.0.1", 79 | "@rollup/plugin-node-resolve": "13.0.4", 80 | "@rollup/plugin-replace": "3.0.0", 81 | "@vue/babel-preset-app": "4.5.15", 82 | "@vue/cli-plugin-babel": "4.5.15", 83 | "@vue/cli-service": "4.5.15", 84 | "@vue/compiler-sfc": "3.2.31", 85 | "@vue/test-utils": "2.0.0-rc.17", 86 | "babel-eslint": "10.1.0", 87 | "babel-jest": "26.6.3", 88 | "eslint": "7.32.0", 89 | "eslint-config-prettier": "8.3.0", 90 | "eslint-plugin-prettier": "3.4.0", 91 | "eslint-plugin-vue": "7.15.1", 92 | "husky": "7.0.1", 93 | "jest": "26.6.3", 94 | "jest-canvas-mock": "2.3.1", 95 | "lint-staged": "11.1.1", 96 | "minimist": "1.2.5", 97 | "npm-run-all": "4.1.5", 98 | "postcss": "8.4.6", 99 | "prettier": "2.3.2", 100 | "rimraf": "3.0.2", 101 | "rollup": "2.67.1", 102 | "rollup-plugin-postcss": "4.0.2", 103 | "rollup-plugin-size-snapshot": "0.12.0", 104 | "rollup-plugin-terser": "7.0.2", 105 | "rollup-plugin-vue": "6.0.0", 106 | "vue": "3.2.0", 107 | "vue-jest": "5.0.0-alpha.10" 108 | }, 109 | "peerDependencies": { 110 | "vue": "^3.2.0" 111 | }, 112 | "engines": { 113 | "node": ">=12" 114 | }, 115 | "license": "MIT" 116 | } 117 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:js-lib" 4 | ], 5 | "ignoreDeps": ["canvas"] 6 | } 7 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import babel from '@rollup/plugin-babel'; 3 | import replace from '@rollup/plugin-replace'; 4 | import commonjs from '@rollup/plugin-commonjs'; 5 | import resolve from '@rollup/plugin-node-resolve'; 6 | import vue from 'rollup-plugin-vue'; 7 | import postCSS from 'rollup-plugin-postcss'; 8 | import { terser } from 'rollup-plugin-terser'; 9 | 10 | import { sizeSnapshot } from 'rollup-plugin-size-snapshot'; 11 | import minimist from 'minimist'; 12 | 13 | const esbrowserslist = fs 14 | .readFileSync('./.browserslistrc') 15 | .toString() 16 | .split('\n') 17 | .filter(entry => entry && entry.substring(0, 2) !== 'ie'); 18 | 19 | const argv = minimist(process.argv.slice(2)); 20 | 21 | const external = ['vue', 'merge-images', 'signature_pad']; 22 | const globals = { 23 | vue: 'Vue', 24 | signature_pad: 'SignaturePad', 25 | 'merge-images': 'mergeImages' 26 | }; 27 | 28 | const baseConfig = { 29 | input: 'src/entry.js', 30 | plugins: { 31 | replace: { 32 | preventAssignment: true, 33 | 'process.env.NODE_ENV': JSON.stringify('production') 34 | }, 35 | postVue: [ 36 | resolve({ 37 | extensions: ['.js', '.jsx', '.ts', '.tsx', '.vue'] 38 | }), 39 | postCSS({ 40 | modules: { 41 | generateScopedName: '[local]___[hash:base64:5]' 42 | }, 43 | include: /&module=.*\.css$/ 44 | }), 45 | postCSS({ include: /(? 2 | import { h, defineComponent } from 'vue'; 3 | import SignaturePad from 'signature_pad'; 4 | import mergeImages from 'merge-images'; 5 | import { 6 | DEFAULT_OPTIONS, 7 | TRANSPARENT_PNG, 8 | IMAGE_TYPES, 9 | checkSaveType, 10 | convert2NonReactive 11 | } from '../utils/index'; 12 | 13 | export default defineComponent({ 14 | name: 'VueSignaturePad', 15 | props: { 16 | width: { 17 | type: String, 18 | default: '100%' 19 | }, 20 | height: { 21 | type: String, 22 | default: '100%' 23 | }, 24 | customStyle: { 25 | type: Object, 26 | default: () => ({}) 27 | }, 28 | options: { 29 | type: Object, 30 | default: () => ({}) 31 | }, 32 | images: { 33 | type: Array, 34 | default: () => [] 35 | }, 36 | scaleToDevicePixelRatio: { 37 | type: Boolean, 38 | default: () => true 39 | } 40 | }, 41 | 42 | data: () => ({ 43 | signaturePad: {}, 44 | cacheImages: [], 45 | signatureData: TRANSPARENT_PNG, 46 | onResizeHandler: null 47 | }), 48 | 49 | computed: { 50 | propsImagesAndCustomImages() { 51 | const nonReactiveProrpImages = convert2NonReactive(this.images); 52 | const nonReactiveCachImages = convert2NonReactive(this.cacheImages); 53 | 54 | return [...nonReactiveProrpImages, ...nonReactiveCachImages]; 55 | } 56 | }, 57 | 58 | watch: { 59 | options: function (nextOptions) { 60 | Object.keys(nextOptions).forEach(option => { 61 | if (this.signaturePad[option]) { 62 | this.signaturePad[option] = nextOptions[option]; 63 | } 64 | }); 65 | } 66 | }, 67 | 68 | mounted() { 69 | const { options } = this; 70 | const canvas = this.$refs.signaturePadCanvas; 71 | const signaturePad = new SignaturePad(canvas, { 72 | ...DEFAULT_OPTIONS, 73 | ...options 74 | }); 75 | this.signaturePad = signaturePad; 76 | 77 | if (options.resizeHandler) { 78 | this.resizeCanvas = options.resizeHandler.bind(this); 79 | } 80 | 81 | this.onResizeHandler = this.resizeCanvas.bind(this); 82 | 83 | window.addEventListener('resize', this.onResizeHandler, false); 84 | 85 | this.resizeCanvas(); 86 | }, 87 | 88 | beforeUnmount() { 89 | if (this.onResizeHandler) { 90 | window.removeEventListener('resize', this.onResizeHandler, false); 91 | } 92 | }, 93 | 94 | methods: { 95 | resizeCanvas() { 96 | const canvas = this.$refs.signaturePadCanvas; 97 | const data = this.signaturePad.toData(); 98 | const ratio = this.scaleToDevicePixelRatio 99 | ? Math.max(window.devicePixelRatio || 1, 1) 100 | : 1; 101 | 102 | canvas.width = canvas.offsetWidth * ratio; 103 | canvas.height = canvas.offsetHeight * ratio; 104 | canvas.getContext('2d').scale(ratio, ratio); 105 | 106 | this.signaturePad.clear(); 107 | this.signatureData = TRANSPARENT_PNG; 108 | this.signaturePad.fromData(data); 109 | }, 110 | 111 | saveSignature(type = IMAGE_TYPES[0], encoderOptions) { 112 | const { signaturePad } = this; 113 | const status = { isEmpty: false, data: undefined }; 114 | 115 | if (!checkSaveType(type)) { 116 | const imageTypesString = IMAGE_TYPES.join(', '); 117 | throw new Error( 118 | `The Image type is incorrect! We are support ${imageTypesString} types.` 119 | ); 120 | } 121 | 122 | if (signaturePad.isEmpty()) { 123 | return { 124 | ...status, 125 | isEmpty: true 126 | }; 127 | } else { 128 | this.signatureData = signaturePad.toDataURL(type, encoderOptions); 129 | 130 | return { 131 | ...status, 132 | data: this.signatureData 133 | }; 134 | } 135 | }, 136 | 137 | undoSignature() { 138 | const { signaturePad } = this; 139 | const record = signaturePad.toData(); 140 | 141 | if (record) { 142 | return signaturePad.fromData(record.slice(0, -1)); 143 | } 144 | }, 145 | 146 | mergeImageAndSignature(customSignature) { 147 | this.signatureData = customSignature; 148 | 149 | return mergeImages([ 150 | ...this.images, 151 | ...this.cacheImages, 152 | this.signatureData 153 | ]); 154 | }, 155 | 156 | addImages(images = []) { 157 | this.cacheImages = [...this.cacheImages, ...images]; 158 | 159 | return mergeImages([ 160 | ...this.images, 161 | ...this.cacheImages, 162 | this.signatureData 163 | ]); 164 | }, 165 | 166 | fromDataURL(data, options = {}, callback) { 167 | return this.signaturePad.fromDataURL(data, options, callback); 168 | }, 169 | 170 | fromData(data) { 171 | return this.signaturePad.fromData(data); 172 | }, 173 | 174 | toData() { 175 | return this.signaturePad.toData(); 176 | }, 177 | 178 | lockSignaturePad() { 179 | return this.signaturePad.off(); 180 | }, 181 | 182 | openSignaturePad() { 183 | return this.signaturePad.on(); 184 | }, 185 | 186 | isEmpty() { 187 | return this.signaturePad.isEmpty(); 188 | }, 189 | 190 | getPropImagesAndCacheImages() { 191 | return this.propsImagesAndCustomImages; 192 | }, 193 | 194 | clearCacheImages() { 195 | this.cacheImages = []; 196 | 197 | return this.cacheImages; 198 | }, 199 | 200 | clearSignature() { 201 | return this.signaturePad.clear(); 202 | } 203 | }, 204 | 205 | render() { 206 | const { width, height, customStyle } = this; 207 | 208 | return h( 209 | 'div', 210 | { 211 | style: { 212 | width, 213 | height, 214 | ...customStyle 215 | } 216 | }, 217 | [ 218 | h('canvas', { 219 | style: { 220 | width: width, 221 | height: height 222 | }, 223 | ref: 'signaturePadCanvas' 224 | }) 225 | ] 226 | ); 227 | } 228 | }); 229 | 230 | -------------------------------------------------------------------------------- /src/components/__tests__/VueSignaturePad.spec.js: -------------------------------------------------------------------------------- 1 | import { shallowMount } from '@vue/test-utils'; 2 | import VueSignaturePad from '../VueSignaturePad'; 3 | import { 4 | signatureMockData, 5 | mockEncodeDataURL, 6 | signatureMockDataPoints 7 | } from './mock'; 8 | 9 | describe('VueSignaturePad Component', () => { 10 | it('should be receive default props', () => { 11 | const wrapper = shallowMount(VueSignaturePad); 12 | const expectedWidth = '100%'; 13 | const expectedHeight = '100%'; 14 | const expectedOptions = {}; 15 | const expectedImages = []; 16 | 17 | expect(wrapper.props().width).toBe(expectedWidth); 18 | expect(wrapper.props().height).toBe(expectedHeight); 19 | expect(wrapper.props().options).toEqual(expectedOptions); 20 | expect(wrapper.props().images).toEqual(expectedImages); 21 | }); 22 | 23 | it('should be throw incorrect image error message', () => { 24 | const addOptionsWrapper = shallowMount(VueSignaturePad); 25 | 26 | expect(() => addOptionsWrapper.vm.saveSignature('text/html')).toThrow(); 27 | }); 28 | 29 | it('should be return empty status and undefined data', () => { 30 | const wrapper = shallowMount(VueSignaturePad); 31 | 32 | expect(wrapper.vm.saveSignature()).toEqual({ 33 | isEmpty: true, 34 | data: undefined 35 | }); 36 | }); 37 | 38 | it('should be return signaturePad status and data', () => { 39 | const wrapper = shallowMount(VueSignaturePad); 40 | 41 | wrapper.setData({ 42 | signaturePad: { 43 | _data: signatureMockData, 44 | isEmpty() { 45 | return false; 46 | }, 47 | toDataURL() { 48 | return mockEncodeDataURL; 49 | } 50 | } 51 | }); 52 | 53 | expect(wrapper.vm.saveSignature()).toEqual({ 54 | isEmpty: false, 55 | data: mockEncodeDataURL 56 | }); 57 | }); 58 | 59 | it('should be return signature data array', () => { 60 | const wrapper = shallowMount(VueSignaturePad); 61 | 62 | wrapper.setData({ 63 | signaturePad: { 64 | _data: signatureMockData 65 | }, 66 | toData() { 67 | return signatureMockData; 68 | } 69 | }); 70 | 71 | expect(wrapper.vm.toData()).toEqual(signatureMockData); 72 | }); 73 | 74 | it('should be set signature from data array', () => { 75 | const wrapper = shallowMount(VueSignaturePad); 76 | 77 | expect(wrapper.vm.fromData(signatureMockDataPoints)).toEqual(undefined); 78 | expect(wrapper.vm.toData()).toEqual(signatureMockDataPoints); 79 | }); 80 | 81 | it('should be throw incorrect data array', () => { 82 | const wrapper = shallowMount(VueSignaturePad); 83 | 84 | expect(() => wrapper.vm.fromData('Not an array')).toThrow(); 85 | }); 86 | 87 | it('should be undo draw action', () => { 88 | const wrapper = shallowMount(VueSignaturePad); 89 | 90 | wrapper.setData({ 91 | signaturePad: { 92 | _data: signatureMockData, 93 | isEmpty() { 94 | return false; 95 | }, 96 | toData() { 97 | return signatureMockData; 98 | }, 99 | toDataURL(type, ...options) { 100 | return mockEncodeDataURL; 101 | }, 102 | fromData(data) { 103 | return data; 104 | } 105 | } 106 | }); 107 | 108 | expect(wrapper.vm.undoSignature()).toEqual([]); 109 | }); 110 | 111 | it('should be lock or open signatrue pad', () => { 112 | const wrapper = shallowMount(VueSignaturePad); 113 | 114 | wrapper.setData({ 115 | signaturePad: { 116 | _data: signatureMockData, 117 | isEmpty() { 118 | return false; 119 | }, 120 | toData() { 121 | return signatureMockData; 122 | }, 123 | toDataURL(type, ...options) { 124 | return mockEncodeDataURL; 125 | }, 126 | fromData(data) { 127 | return data; 128 | }, 129 | off() { 130 | return 'lock'; 131 | }, 132 | on() { 133 | return 'open'; 134 | } 135 | } 136 | }); 137 | 138 | expect(wrapper.vm.lockSignaturePad()).toBe('lock'); 139 | expect(wrapper.vm.openSignaturePad()).toBe('open'); 140 | }); 141 | 142 | it('should be get props images and cache images', () => { 143 | const wrapper = shallowMount(VueSignaturePad); 144 | 145 | wrapper.setData({ cacheImages: ['foo', 'bar'] }); 146 | expect(wrapper.vm.getPropImagesAndCacheImages()).toEqual(['foo', 'bar']); 147 | }); 148 | 149 | it('should be clear cache images', () => { 150 | const wrapper = shallowMount(VueSignaturePad); 151 | 152 | wrapper.setData({ cacheImages: ['foo', 'bar'] }); 153 | expect(wrapper.vm.getPropImagesAndCacheImages()).toEqual(['foo', 'bar']); 154 | 155 | wrapper.vm.clearCacheImages(); 156 | expect(wrapper.vm.cacheImages).toEqual([]); 157 | }); 158 | 159 | it('should be clear signatrue', () => { 160 | const wrapper = shallowMount(VueSignaturePad); 161 | 162 | wrapper.setData({ 163 | signaturePad: { 164 | clear() { 165 | return true; 166 | } 167 | } 168 | }); 169 | 170 | expect(wrapper.vm.clearSignature()).toBe(true); 171 | }); 172 | 173 | it('should be read signature from given data', () => { 174 | const giveSignatureData = 175 | 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII='; 176 | 177 | const wrapper = shallowMount(VueSignaturePad, { 178 | propsData: { 179 | giveSignatureData 180 | } 181 | }); 182 | 183 | wrapper.setData({ 184 | signaturePad: { 185 | fromDataURL(data) { 186 | return data; 187 | } 188 | } 189 | }); 190 | 191 | expect(wrapper.vm.fromDataURL(giveSignatureData)).toEqual( 192 | giveSignatureData 193 | ); 194 | }); 195 | 196 | it('should be return siganture pad empty status', () => { 197 | const wrapper = shallowMount(VueSignaturePad); 198 | 199 | wrapper.setData({ 200 | signaturePad: { 201 | _data: '', 202 | isEmpty() { 203 | return this._data.length > 0 ? false : true; 204 | } 205 | } 206 | }); 207 | 208 | expect(wrapper.vm.isEmpty()).toBe(true); 209 | }); 210 | }); 211 | -------------------------------------------------------------------------------- /src/components/__tests__/mock.js: -------------------------------------------------------------------------------- 1 | export const signatureMockData = [ 2 | [ 3 | { x: 101, y: 68, time: 1518915273087, color: 'black' }, 4 | { x: 106, y: 72, time: 1518915273169, color: 'black' }, 5 | { x: 111, y: 81, time: 1518915273202, color: 'black' }, 6 | { x: 117, y: 88, time: 1518915273218, color: 'black' }, 7 | { x: 121, y: 95, time: 1518915273235, color: 'black' }, 8 | { x: 129, y: 109, time: 1518915273253, color: 'black' }, 9 | { x: 132, y: 113, time: 1518915273269, color: 'black' }, 10 | { x: 135, y: 118, time: 1518915273320, color: 'black' }, 11 | { x: 142, y: 118, time: 1518915273524, color: 'black' }, 12 | { x: 152, y: 111, time: 1518915273541, color: 'black' }, 13 | { x: 170, y: 100, time: 1518915273557, color: 'black' }, 14 | { x: 192, y: 86, time: 1518915273574, color: 'black' }, 15 | { x: 224, y: 67, time: 1518915273590, color: 'black' }, 16 | { x: 238, y: 59, time: 1518915273606, color: 'black' }, 17 | { x: 249, y: 54, time: 1518915273622, color: 'black' }, 18 | { x: 258, y: 49, time: 1518915273639, color: 'black' }, 19 | { x: 267, y: 44, time: 1518915273656, color: 'black' }, 20 | { x: 274, y: 40, time: 1518915273690, color: 'black' }, 21 | { x: 258, y: 49, time: 1518915273639, color: 'black' } 22 | ] 23 | ]; 24 | 25 | export const signatureMockDataPoints = [ 26 | { 27 | color: 'black', 28 | points: [ 29 | { 30 | x: 141, 31 | y: 71.46875, 32 | time: 1570719315651 33 | }, 34 | { 35 | x: 156, 36 | y: 71.46875, 37 | time: 1570719315747 38 | }, 39 | { 40 | x: 180, 41 | y: 69.46875, 42 | time: 1570719315763 43 | }, 44 | { 45 | x: 207, 46 | y: 69.46875, 47 | time: 1570719315780 48 | }, 49 | { 50 | x: 232, 51 | y: 69.46875, 52 | time: 1570719315798 53 | }, 54 | { 55 | x: 256, 56 | y: 69.46875, 57 | time: 1570719315815 58 | }, 59 | { 60 | x: 277, 61 | y: 69.46875, 62 | time: 1570719315831 63 | }, 64 | { 65 | x: 299, 66 | y: 69.46875, 67 | time: 1570719315848 68 | }, 69 | { 70 | x: 323, 71 | y: 69.46875, 72 | time: 1570719315865 73 | }, 74 | { 75 | x: 354, 76 | y: 69.46875, 77 | time: 1570719315882 78 | }, 79 | { 80 | x: 372, 81 | y: 69.46875, 82 | time: 1570719315899 83 | }, 84 | { 85 | x: 386, 86 | y: 69.46875, 87 | time: 1570719315915 88 | }, 89 | { 90 | x: 397, 91 | y: 69.46875, 92 | time: 1570719315931 93 | }, 94 | { 95 | x: 404, 96 | y: 69.46875, 97 | time: 1570719315947 98 | }, 99 | { 100 | x: 416, 101 | y: 69.46875, 102 | time: 1570719315983 103 | }, 104 | { 105 | x: 427, 106 | y: 69.46875, 107 | time: 1570719316000 108 | }, 109 | { 110 | x: 441, 111 | y: 69.46875, 112 | time: 1570719316017 113 | }, 114 | { 115 | x: 457, 116 | y: 69.46875, 117 | time: 1570719316034 118 | }, 119 | { 120 | x: 473, 121 | y: 69.46875, 122 | time: 1570719316050 123 | }, 124 | { 125 | x: 487, 126 | y: 69.46875, 127 | time: 1570719316066 128 | }, 129 | { 130 | x: 504, 131 | y: 69.46875, 132 | time: 1570719316082 133 | }, 134 | { 135 | x: 510, 136 | y: 69.46875, 137 | time: 1570719316098 138 | }, 139 | { 140 | x: 529, 141 | y: 69.46875, 142 | time: 1570719316115 143 | }, 144 | { 145 | x: 541, 146 | y: 69.46875, 147 | time: 1570719316131 148 | }, 149 | { 150 | x: 549, 151 | y: 69.46875, 152 | time: 1570719316148 153 | }, 154 | { 155 | x: 556, 156 | y: 69.46875, 157 | time: 1570719316182 158 | }, 159 | { 160 | x: 562, 161 | y: 69.46875, 162 | time: 1570719316216 163 | }, 164 | { 165 | x: 571, 166 | y: 69.46875, 167 | time: 1570719316249 168 | }, 169 | { 170 | x: 577, 171 | y: 70.46875, 172 | time: 1570719316284 173 | }, 174 | { 175 | x: 585, 176 | y: 69.46875, 177 | time: 1570719316317 178 | }, 179 | { 180 | x: 590, 181 | y: 70.46875, 182 | time: 1570719316335 183 | }, 184 | { 185 | x: 595, 186 | y: 72.46875, 187 | time: 1570719316352 188 | }, 189 | { 190 | x: 608, 191 | y: 74.46875, 192 | time: 1570719316394 193 | }, 194 | { 195 | x: 613, 196 | y: 75.46875, 197 | time: 1570719316426 198 | }, 199 | { 200 | x: 619, 201 | y: 76.46875, 202 | time: 1570719316514 203 | } 204 | ] 205 | } 206 | ]; 207 | 208 | export const mockEncodeDataURL = 209 | 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAGQCAYAAABWJQQ0AAAgAElEQVR4Xu3dPY8cadk24N7oAQmtTbQisi0RIo2dsKHtBK1E4J2UZHZEijT2H1gzIiAgMPMHsCciNBNuZFsIicxrJDIkr3+BcQBIJPvo7PfpV95xf1TPdFffddVREgIxPVX3fVxlac6+vz769ttvv524CBAgQIAAAQIECBAg0IPARwJID8oeQYAAAQIECBAgQIDAVEAA8SIQIECAAAECBAgQINCbgADSG7UHESBAgAABAgQIECAggHgHCBAgQIAAAQIECBDoTUAA6Y3agwgQIECAAAECBAgQEEC8AwQIECBAgAABAgQI9CYggPRG7UEECBAgQIAAAQIECAgg3gECBAgQIECAAAECBHoTEEB6o/YgAgQIECBAgAABAgQEEO8AAQIECBAgQIAAAQK9CQggvVF7EAECBAgQIECAAAECAoh3gAABAgQIECBAgACB3gQEkN6oPYgAAQIECBAgQIAAAQHEO0CAAAECBAgQIECAQG8CAkhv1B5EgAABAgQIECBAgIAA4h0gQIAAAQIECBAgQKA3AQGkN2oPIkCAAAECBAgQIEBAAPEOECBAgAABAgQIECDQm4AA0hu1BxEgQIAAAQIECBAgIIB4BwgQIECAAAECBAgQ6E1AAOmN2oMIECBAgAABAgQIEBBAvAMECBAgQIAAAQIECPQmIID0Ru1BBAgQIECAAAECBAgIIN4BAgQIECBAgAABAgR6ExBAeqP2IAIECBAgQIAAAQIEBBDvAAECBAgQIECAAAECvQkIIL1RexABAgQIECBAgAABAgKId4AAAQIECBAgQIAAgd4EBJDeqD2IAAECBAgQIECAAAEBxDtAgAABAgQIECBAgEBvAgJIb9QeRIAAAQIECBAgQICAAOIdIECAAAECBAgQIECgNwEBpDdqDyJAgAABAgQIECBAQADxDhAgQIAAAQIECBAg0JuAANIbtQcRIECAAAECBAgQICCAeAcIECBAgAABAgQIEOhNQADpjdqDCBAgQIAAAQIECBAQQLwDBAgQIECAAAECBAj0JiCA9EbtQQQIECBAgAABAgQICCDeAQIECBAgQIAAAQIEehMQQHqj9iACBAgQIECAAAECBAQQ7wABAgQIECBAgAABAr0JCCC9UXsQAQIECBAgQIAAAQICiHeAAAECBAgQIECAAIHeBASQ3qg9iAABAgQIECBAgAABAcQ7QIAAAQIECBAgQIBAbwICSG/UHkSAAAECBAgQIECAgADiHSBAgAABAgQIECBAoDcBAaQ3ag8iQIAAAQIECBAgQEAA8Q4QIECAAAECBAgQINCbgADSG7UHESBAgAABAgQIECAggHgHCBAgQIAAAQIECBDoTUAA6Y3agwgQIECAAAECBAgQEEC8AwQIECBAgAABAgQI9CYggPRG7UEECBAgQIAAAQIECAgg3gECBAgQIECAAAECBHoTEEB6o/YgAgQIECBAgAABAgQEEO8AAQIECBAgQIAAAQK9CQggvVF7EAECBAgQIECAAAECAoh3gAABAgQIECBAgACB3gQEkN6oPYgAAQIECBAgQIAAAQHEO0CAAAECBAgQIECAQG8CAkhv1B5EgAABAgQIECBAgIAA4h0gQIAAAQIECBAgQKA3AQGkN2oPIkCAAAECBAgQIEBAAPEOECBAgAABAgQIECDQm4AA0hu1BxEgQIAAAQIECBAgIIB4BwgQIECAAAECBAgQ6E1AAOmN2oMIECBAgAABAgQIEBBAvAMECBAgQIAAAQIECPQmIID0Ru1BBAgQIECAAAECBAgIIN4BAgQIECBAgAABAgR6ExBAeqP2IAIECBAgQIAAAQIEBBDvAAECBAgQIECAAAECvQkIIL1RexABAgQIECBAgAABAgKId4AAAQIECBAgQIAAgd4EBJDeqD2IAAECBAgQIECAAAEBxDtAgAABAgQIECBAgEBvAgJIb9QeRIAAAQIECBAgQICAAOIdIECAAAECBAgQIECgNwEBpDdqDyJAgAABAgQIECBAQADxDhAgQIAAAQIECBAg0JuAANIbtQcRIECAAAECBAgQICCAeAcIECBAgAABAgQIEOhNQADpjdqDCBAgQIAAAQIECBAQQLwDBAgQIECAwIUE/vnPf05OTk4m33zzzeT69euTg4OD6X+7CBAgsExAAPF+ECBAgAABAmsL/OlPf5ocHh5OEkJm19WrVycvX74UQtbW9AsExiUggIyr3npLgAABAgQuJfD8+fPJgwcPJl9//fXc+zx8+HDy61//+lLP8MsECNQWEEBq11fvCBAgQIDARgQSOI6PjycZ+Vh2CSAb4XYTAqUFBJDS5dU5AgQIECBwOYFMscqIx5MnTzrd6NGjR5P7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IECAAAECpQQEkFLl1BkCBAgQIECAAAECbQsIIG3XR+sIECBAgAABAgQIlBIQQEqVU2cIECBAgAABAgQItC0ggLRdH60jQIAAAQIECBAgUEpAAClVTp0hQIAAAQIECBAg0LaAANJ2fbSOAAECBAgQIECAQCkBAaRUOXWGAAECBAgQIECAQNsCAkjb9dE6AgQIECBAgAABAqUEBJBS5dQZAgQIECBAgAABAm0LCCBt10frCBAgQIAAAQIECJQSEEBKlVNnCBAgQIAAAQIECLQtIIC0XR+tI0CAAAECBAgQIFBKQAApVU6dIUCAAAECBAgQINC2gADSdn20jgABAgQIECBAgEApAQGkVDl1hgABAgQIECBAgEDbAgJI2/XROgIECBAgQIAAAQKlBASQUuXUGQIECBAgQIAAAQJtCwggbddH6wgQIECAAAECBAiUEhBASpVTZwgQIECAAAECBAi0LSCAtF0frSNAgAABAgQIECBQSkAAKVVOnSFAgAABAgQIECDQtoAA0nZ9tI4AAQIECBAgQIBAKQEBpFQ5dYYAAQIECBAgQIBA2wICSNv10ToCBAgQIECAAAECpQQEkFLl1BkCBAgQIECAAAECbQsIIG3XR+sIECBAgAABAgQIlBIQQEqVU2cIECBAgAABAgQItC0ggLRdH60jQIAAAQIECBAgUEpAAClVTp0hQIAAAQIECBAg0LaAANJ2fbSOAAECBAgQIECAQCkBAaRUOXWGAAECBAgQIECAQNsCAkjb9dE6AgQIECBAgAABAqUEBJBS5dQZAgQIECBAgAABAm0LCCBt10frCBAgQIAAAQIECJQSEEBKlVNnCBAgQIAAAQIECLQtIIC0XR+tI0CAAAECBAgQIFBKQAApVU6dIUCAAAECBAgQINC2gADSdn20jgABAgQIECBAgEApAQGkVDl1hgABAgQIECBAgEDbAgJI2/XROgIECBAgQIAAAQKlBASQUuXUGQIECBAgQIAAAQJtCwggbddH6wgQIECAAAECBAiUEhBASpVTZwgQIECAAAECBAi0LSCAtF0frSNAgAABAgQIECBQSkAAKVVOnSFAgAABAgQIECDQtoAA0nZ9tI4AAQIECBAgQIBAKQEBpFQ5dYYAAQIECBAgQIBA2wICSNv10ToCBAgQIECAAAECpQQEkFLl1BkCBAgQIECAAAECbQsIIG3XR+sIECBAgAABAgQIlBIQQEqVU2cIECBAgAABAgQItC0ggLRdH60jQIAAAQIECBAgUEpAAClVTp0hQIAAAQIECBAg0LaAANJ2fbSOAAECBAgQIECAQCkBAaRUOXWGAAECBAgQIECAQNsCAkjb9dE6AgQIECBAgAABAqUEBJBS5dQZAgQIECBAgAABAm0LCCBt10frCBAgQIAAAQIECJQSEEBKlVNnCBAgQIAAAQIECLQtIIC0XR+tI0CAAAECBAgQIFBKQAApVU6dIUCAAAECBAgQINC2gADSdn20jgABAgQIECBAgEApAQGkVDl1hgABAgQIECBAgEDbAgJI2/XROgIECBAgQIAAAQKlBASQUuXUGQIECBAgQIAAAQJtCwggbddH6wgQIECAAAECBAiUEhBASpVTZwgQIECAAAECBAi0LSCAtF0frSNAgAABAgQIECBQSkAAKVVOnSFAgAABAgQIECDQtoAA0nZ9tI4AAQIECBAgQIBAKQEBpFQ5dYYAAQIECBAgQIBA2wICSNv10ToCBAgQIECAAAECpQQEkFLl1BkCBAgQIECAAAECbQsIIG3XR+sIECBAgAABAgQIlBIQQEqVU2cIECBAgAABAgQItC0ggLRdH60jQIAAAQIECBAgUEpAAClVTp0hQIAAAQIECBAg0LaAANJ2fbSOAAECBAgQIECAQCkBAaRUOXWGAAECBAgQIECAQNsCAkjb9dE6AgQIECBAgAABAqUEBJBS5dQZAgQIECBAgAABAm0LCCBt10frCBAgQIAAAQIECJQSEEBKlVNnCBAgQIAAAQIECLQtIIC0XR+tI0CAAAECBAgQIFBKQAApVU6dIUCAAAECBAgQINC2gADSdn20jgABAgQIECBAgEApAQGkVDl1hgABAgQIECBAgEDbAgJI2/XROgIECBAgQIAAAQKlBASQUuXUGQIECBAgQIAAAQJtCwggbddH6wgQIECAAAECBAiUEhBASpVTZwgQIECAAAECBAi0LSCAtF0frSNAgAABAgQIECBQSkAAKVVOnSFAgAABAgQIECDQtoAA0nZ9tI4AAQIECBAgQIBAKQEBpFQ5dYYAAQIECBAgQIBA2wICSNv10ToCBAgQIECAAAECpQQEkFLl1BkCBAgQIECAAAECbQsIIG3XR+sIECBAgAABAgQIlBIQQEqVU2cIECBAgAABAgQItC0ggLRdH60jQIAAAQIECBAgUEpAAClVTp0hQIAAAQIECBAg0LaAANJ2fbSOAAECBAgQIECAQCkBAaRUOXWGAAECBAgQIECAQNsCAkjb9dE6AgQIECBAgAABAqUEBJBS5dQZAgQIECBAgAABAm0LCCBt10frCBAgQIAAAQIECJQSEEBKlVNnCBAgQIAAAQIECLQtIIC0XR+tI0CAAAECBAgQIFBKQAApVU6dIUCAAAECBAgQINC2gADSdn20jgABAgQIECBAgEApAQGkVDl1hgABAgQIECBAgEDbAgJI2/XROgIECBAgQIAAAQKlBASQUuXUGQIECBAgQIAAAQJtCwggbddH6wgQIECAAAECBAiUEhBASpVTZwgQIECAAAECBAi0LSCAtF0frSNAgAABAgQIECBQSkAAKVVOnSFAgAABAgQIECDQtoAA0nZ9tI4AAQIECBAgQIBAKQEBpFQ5dYYAAQIECBAgQIBA2wICSNv10ToCBAgQIECAAAECpQQEkFLl1BkCBAgQIECAAAECbQsIIG3XR+sIECBAgAABAgQIlBIQQEqVU2cIECBAgAABAgQItC0ggLRdH60jQIAAAQIECBAgUEpAAClVTp0hQIAAAQIECBAg0LaAANJ2fbSOAAECBAgQIECAQCkBAaRUOXWGAAECBAgQIECAQNsCAkjb9dE6AgQIECBAgAABAqUEBJBS5dQZAgQIECBAgAABAm0LCCBt10frCBAgQIAAAQIECJQSEEBKlVNnCBAgQIAAAQIECLQtIIC0XR+tI0CAAAECBAgQIFBKQAApVU6dIUCAAAECBAgQINC2gADSdn20jgABAgQIECBAgEApAQGkVDl1hgABAgQIECBAgEDbAgJI2/XROgIECBAgQIAAAQKlBASQUuXUGQIECBAgQIAAAQJtCwggbddH6wgQIECAAAECBAiUEhBASpVTZwgQIECAAAECBAi0LSCAtF0frSNAgAABAgQIECBQSkAAKVVOnSFAgAABAgQIECDQtoAA0nZ9tI4AAQIECBAgQIBAKQEBpFQ5dYYAAQIECBAgQIBA2wL/C7lkTCPxUuULAAAAAElFTkSuQmCC'; 210 | -------------------------------------------------------------------------------- /src/components/index.js: -------------------------------------------------------------------------------- 1 | export { default as VueSignaturePad } from './VueSignaturePad.vue'; 2 | -------------------------------------------------------------------------------- /src/entry.esm.js: -------------------------------------------------------------------------------- 1 | import * as components from './components'; 2 | 3 | const install = function installVSignature(app) { 4 | Object.entries(components).forEach(([componentName, component]) => { 5 | app.component(componentName, component); 6 | }); 7 | }; 8 | 9 | export default install; 10 | 11 | export * from './components'; 12 | -------------------------------------------------------------------------------- /src/entry.js: -------------------------------------------------------------------------------- 1 | import plugin, * as components from './entry.esm'; 2 | 3 | Object.entries(components).forEach(([componentName, component]) => { 4 | if (componentName !== 'default') { 5 | const key = componentName; 6 | const val = component; 7 | 8 | plugin[key] = val; 9 | } 10 | }); 11 | 12 | export default plugin; 13 | -------------------------------------------------------------------------------- /src/utils/__tests__/index.spec.js: -------------------------------------------------------------------------------- 1 | import { checkSaveType } from '../'; 2 | 3 | describe('checkSaveType', () => { 4 | it('should return true when given valid type', () => { 5 | const validType1 = 'image/png'; 6 | const validType2 = 'image/jpeg'; 7 | const validType3 = 'image/svg+xml'; 8 | 9 | expect(checkSaveType(validType1)).toBe(true); 10 | expect(checkSaveType(validType2)).toBe(true); 11 | expect(checkSaveType(validType3)).toBe(true); 12 | }); 13 | 14 | it('should return false when given invalid type', () => { 15 | const invalidType = 'text/plain'; 16 | expect(checkSaveType(invalidType)).toBe(false); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/utils/index.js: -------------------------------------------------------------------------------- 1 | export const IMAGE_TYPES = ['image/png', 'image/jpeg', 'image/svg+xml']; 2 | 3 | export const checkSaveType = type => IMAGE_TYPES.includes(type); 4 | 5 | export const DEFAULT_OPTIONS = { 6 | dotSize: (0.5 + 2.5) / 2, 7 | minWidth: 0.5, 8 | maxWidth: 2.5, 9 | throttle: 16, 10 | minDistance: 5, 11 | backgroundColor: 'rgba(0,0,0,0)', 12 | penColor: 'black', 13 | velocityFilterWeight: 0.7, 14 | onBegin: () => {}, 15 | onEnd: () => {} 16 | }; 17 | 18 | export const convert2NonReactive = observerValue => 19 | JSON.parse(JSON.stringify(observerValue)); 20 | 21 | export const TRANSPARENT_PNG = { 22 | src: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=', 23 | x: 0, 24 | y: 0 25 | }; 26 | --------------------------------------------------------------------------------