├── .babelrc
├── .eslintrc
├── .github
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── ISSUE_TEMPLATE
│ ├── config.yml
│ ├── bug.md
│ ├── feature.md
│ └── support.md
└── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── .sass-lint.yml
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── dist
├── css
│ └── app.css
├── fonts
│ └── text-for-mobile.4d297d.svg
├── images
│ ├── dragndrop-icon.svg
│ ├── favicons
│ │ ├── favico-128.png
│ │ ├── favico-152.png
│ │ ├── favico-16.png
│ │ ├── favico-167.png
│ │ ├── favico-180.png
│ │ ├── favico-192.png
│ │ ├── favico-196.png
│ │ └── favico-32.png
│ ├── text-for-mobile.svg
│ └── web-preview.jpg
└── js
│ └── app.js
├── index.html
├── package-lock.json
├── package.json
├── postcss.config.js
├── src
├── images
│ ├── dragndrop-icon.svg
│ ├── favicons
│ │ ├── favico-128.png
│ │ ├── favico-152.png
│ │ ├── favico-16.png
│ │ ├── favico-167.png
│ │ ├── favico-180.png
│ │ ├── favico-192.png
│ │ ├── favico-196.png
│ │ └── favico-32.png
│ ├── text-for-mobile.svg
│ └── web-preview.jpg
├── index.html
├── js
│ ├── chunks
│ │ ├── changeTheme.js
│ │ ├── downloadAsFile.js
│ │ ├── dropzone.js
│ │ ├── generateMeshPoints.js
│ │ ├── isTouch.js
│ │ ├── iterpritateSmoothness.js
│ │ ├── loader.js
│ │ ├── moveCanvas.js
│ │ ├── saveResults.js
│ │ ├── svg-test-string.js
│ │ └── toggleControls.js
│ └── index.js
└── scss
│ ├── _base.scss
│ ├── _controls.scss
│ ├── _dragndrop-zone.scss
│ ├── _if-mobile.scss
│ ├── _loader.scss
│ ├── _mixins.scss
│ ├── _svg.scss
│ ├── _variables.scss
│ ├── normalize.css
│ └── styles.scss
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["@babel/preset-env"]
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "airbnb",
3 | "env": {
4 | "node": true,
5 | "jasmine": true,
6 | "jquery": true
7 | },
8 | "rules": {
9 | "no-use-before-define": 0,
10 | "func-names": 0,
11 | "prefer-arrow-callback": 0,
12 | "no-var": 0,
13 | "max-len": 0,
14 | "guard-for-in": 0,
15 | "object-shorthand": 0,
16 | "no-restricted-syntax": 0,
17 | "prefer-template": 0,
18 | "import/no-amd": 0,
19 | "space-before-function-paren": 0,
20 | "jsx-a11y/href-no-hash": "off",
21 | "jsx-a11y/anchor-is-valid": ["warn", { "aspects": ["invalidHref"] }],
22 | "import/no-unresolved": 0,
23 | "import/extensions": 0
24 | },
25 | "globals": {
26 | "browser": false,
27 | "window": true,
28 | "document": true
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/.github/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at we@athlonproduction.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## How to contribute to Athlon Frontend Webpack Boilerplate
2 |
3 | Thank you for contributing to this template!
4 | =========================================
5 |
6 | ## Language
7 |
8 | English is the best language to communicate.
9 |
10 | ## Templates
11 |
12 | Please use issue/PR templates which are inserted automatically.
13 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/ config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: GitHub Security Bug Bounty
4 | url: https://weareathlon.com/
5 | about: Please report security vulnerabilities here.
6 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug Report
3 | about: Create a report to help us improve
4 |
5 | ---
6 |
7 | **Describe the bug**
8 |
9 | A clear and concise description of what the bug is.
10 |
11 | **To Reproduce**
12 |
13 | Steps to reproduce the behavior:
14 | 1. Go to '...'
15 | 2. Click on '....'
16 | 3. Scroll down to '....'
17 | 4. See error
18 |
19 | **Expected behavior**
20 |
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 |
25 | If applicable, add screenshots to help explain your problem.
26 |
27 | **Desktop (please complete the following information):**
28 |
29 | - OS: [e.g. iOS]
30 | - Browser [e.g. chrome, safari]
31 | - Version [e.g. 22]
32 |
33 | **Smartphone (please complete the following information):**
34 |
35 | - Device: [e.g. iPhone6]
36 | - OS: [e.g. iOS8.1]
37 | - Browser [e.g. stock browser, safari]
38 | - Version [e.g. 22]
39 |
40 | **Additional context**
41 |
42 | Add any other context about the problem here.
43 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature Request
3 | about: Suggest an idea for this project
4 |
5 | ---
6 |
7 | **Is your feature request related to a problem? Please describe.**
8 |
9 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
10 |
11 | ***What is the motivation / use case for changing the behavior?**
12 |
13 | Describe an example use case of this feature.
14 |
15 | **Describe the solution you'd like**
16 |
17 | A clear and concise description of what you want to happen.
18 |
19 | **Describe alternatives you've considered**
20 |
21 | A clear and concise description of any alternative solutions or features you've considered.
22 |
23 | **Additional context**
24 |
25 | Add any other context or screenshots about the feature request here.
26 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/support.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Support Request
3 | about: Describe an issue using this repository code
4 |
5 | ---
6 |
7 | **Is your support request related to a problem? Please describe.**
8 |
9 | A clear and concise description of what the problem is.
10 |
11 |
12 | **Describe alternatives you've considered**
13 |
14 | A clear and concise description of any alternative solutions or features you've considered.
15 |
16 | **Additional context**
17 |
18 | Add any other context or screenshots about the support request here.
19 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | * **Please check if the PR fulfills these requirements**
2 |
3 | - [ ] The commit message follows our guidelines
4 | - [ ] Tests for the changes have been added (for bug fixes / features)
5 | - [ ] Docs have been added / updated (for bug fixes / features)
6 |
7 | * **What kind of change does this PR introduce?**
8 |
9 | (_Bug fix, feature, docs update, ..._)
10 |
11 | * **What is the current behavior?**
12 |
13 | (_You can also link to an open issue here_)
14 |
15 | * **What is the new behavior (_if this is a feature change_)?**
16 |
17 | (_Add description here_)
18 |
19 | * **Does this PR introduce a breaking change?**
20 |
21 | (_What changes might users need to make in their existing projects due to this PR?_)
22 |
23 | * **Other information**:
24 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # Mac OS X
4 | .DS_Store
5 | ._.*
6 | ._*
7 |
8 | # Ignore local editor
9 | .project
10 | .settings
11 | .idea
12 | *.swp
13 | tags
14 | nbproject/*
15 |
16 | # Windows
17 | Thumbs.db
18 |
19 | npm-debug.log
20 |
21 | node_modules/
22 |
--------------------------------------------------------------------------------
/.sass-lint.yml:
--------------------------------------------------------------------------------
1 | files:
2 | # formatter: stylish
3 | # include: '**/*.s+(a|c)ss'
4 | ignore:
5 | - '/usr/local/lib/node_modules/**/*.scss'
6 | - './node_modules/**/*.scss'
7 | - 'src/scss/vendor/**/*.scss'
8 | - 'src/scss/base/_reset.scss'
9 | - 'src/scss/base/_print.scss'
10 | - 'src/scss/base/_fonts.scss'
11 | - 'src/scss/base/mixins.scss'
12 | - 'src/scss/dirty/_hacks.scss'
13 | - 'node_modules/**/*.s+(a|c)ss'
14 | - 'node_modules/grunt-sass/node_modules/node-sass/test/fixtures/watching/index.sass'
15 |
16 | rules:
17 | # Extends
18 | extends-before-mixins: 2
19 | extends-before-declarations: 2
20 | placeholder-in-extend: 2
21 |
22 | # Mixins
23 | mixins-before-declarations:
24 | - 2
25 | -
26 | exclude: ['bp']
27 |
28 | # Line Spacing
29 | one-declaration-per-line: 2
30 | empty-line-between-blocks: 2
31 | single-line-per-selector: 2
32 |
33 | # Disallows
34 | no-attribute-selectors: 0
35 | no-color-keywords: 2
36 | no-color-literals: 2
37 | no-combinators: 0
38 | no-css-comments: 0
39 | no-debug: 2
40 | no-disallowed-properties: 0
41 | no-duplicate-properties: 2
42 | no-empty-rulesets: 2
43 | no-extends: 1
44 | no-ids: 2
45 | no-important: 0
46 | no-invalid-hex: 2
47 | no-mergeable-selectors: 2
48 | no-misspelled-properties: 2
49 | no-qualifying-elements:
50 | - 2
51 | -
52 | allow-element-with-attribute: true
53 |
54 | no-trailing-whitespace: 2
55 | no-trailing-zero: 2
56 | no-transition-all: 0
57 | no-universal-selectors: 2
58 | no-url-protocols: 1
59 | no-vendor-prefixes: 2
60 | no-warn: 2
61 | property-units: 0
62 |
63 | # Nesting
64 | force-attribute-nesting: 0
65 | force-element-nesting: 0
66 | force-pseudo-nesting: 0
67 |
68 | # Name Formats
69 | class-name-format: 2
70 | function-name-format: 2
71 | id-name-format: 2
72 | mixin-name-format: 2
73 | placeholder-name-format: 2
74 | variable-name-format: 2
75 |
76 | # Style Guide
77 | attribute-quotes: 2
78 | bem-depth: 0
79 | border-zero:
80 | - 2
81 | -
82 | convention: 'none'
83 |
84 | brace-style: 2
85 | clean-import-paths: 2
86 | empty-args: 0
87 | hex-length: 2
88 | hex-notation: 2
89 | indentation: 2
90 | leading-zero: 2
91 | nesting-depth: 0
92 | property-sort-order: 0
93 | pseudo-element: 2
94 | quotes: 2
95 | shorthand-values: 2
96 | url-quotes: 2
97 | variable-for-property: 0
98 | zero-unit: 2
99 |
100 | # Inner Spacing
101 | space-after-comma: 2
102 | space-before-colon: 2
103 | space-after-colon: 2
104 | space-before-brace: 2
105 | space-before-bang: 2
106 | space-after-bang: 2
107 | space-between-parens: 2
108 | space-around-operator: 2
109 |
110 | # Final Items
111 | trailing-semicolon: 2
112 | final-newline: 2
113 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - node
4 | - lts/*
5 | install:
6 | - npm ci
7 | script:
8 | - npm run lint-sass
9 | - npm run lint-js
10 | - npm run production
11 | deploy:
12 | provider: pages
13 | skip-cleanup: true
14 | github-token: $GITHUB_TOKEN # Set in the settings page of your repository, as a secure variable
15 | keep-history: true
16 | local-dir: dist
17 | on:
18 | branch: master
19 | node: 'lts/*'
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Athlon DPS
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 | # Warp SVG Online
2 |
3 | 
4 |
5 | ## How to Use
6 |
7 | A simple online tool that allows you to warp 🌀, bend, and distort SVG files.
8 |
9 | ### [🎥 WARP SVG In action](https://pavellaptev.github.io/warp-svg/)
10 |
11 | You can use Figma online tool to prepare your SVG file.
12 |
13 | ### [📖 Tutorial](https://www.figma.com/file/RqhYd0CaFD2f9dvz0m360Z/Warp-SVG-Online?node-id=1%3A2)
14 |
15 | ### REFS
16 | Warp code based on [Warp.js](https://github.com/benjamminf/warpjs) and took from [this issue](https://github.com/benjamminf/warpjs/issues/11)
17 |
--------------------------------------------------------------------------------
/dist/css/app.css:
--------------------------------------------------------------------------------
1 | @import url(https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@500&display=swap);
2 | /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
3 | /* Document
4 | ========================================================================== */
5 | /**
6 | * 1. Correct the line height in all browsers.
7 | * 2. Prevent adjustments of font size after orientation changes in iOS.
8 | */
9 | html {
10 | line-height: 1.15;
11 | /* 1 */
12 | -webkit-text-size-adjust: 100%;
13 | /* 2 */ }
14 |
15 | /* Sections
16 | ========================================================================== */
17 | /**
18 | * Remove the margin in all browsers.
19 | */
20 | body {
21 | margin: 0; }
22 |
23 | /**
24 | * Render the `main` element consistently in IE.
25 | */
26 | main {
27 | display: block; }
28 |
29 | /**
30 | * Correct the font size and margin on `h1` elements within `section` and
31 | * `article` contexts in Chrome, Firefox, and Safari.
32 | */
33 | h1 {
34 | font-size: 2em;
35 | margin: 0.67em 0; }
36 |
37 | /* Grouping content
38 | ========================================================================== */
39 | /**
40 | * 1. Add the correct box sizing in Firefox.
41 | * 2. Show the overflow in Edge and IE.
42 | */
43 | hr {
44 | box-sizing: content-box;
45 | /* 1 */
46 | height: 0;
47 | /* 1 */
48 | overflow: visible;
49 | /* 2 */ }
50 |
51 | /**
52 | * 1. Correct the inheritance and scaling of font size in all browsers.
53 | * 2. Correct the odd `em` font sizing in all browsers.
54 | */
55 | pre {
56 | font-family: monospace, monospace;
57 | /* 1 */
58 | font-size: 1em;
59 | /* 2 */ }
60 |
61 | /* Text-level semantics
62 | ========================================================================== */
63 | /**
64 | * Remove the gray background on active links in IE 10.
65 | */
66 | a {
67 | background-color: transparent; }
68 |
69 | /**
70 | * 1. Remove the bottom border in Chrome 57-
71 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
72 | */
73 | abbr[title] {
74 | border-bottom: none;
75 | /* 1 */
76 | text-decoration: underline;
77 | /* 2 */
78 | -webkit-text-decoration: underline dotted;
79 | text-decoration: underline dotted;
80 | /* 2 */ }
81 |
82 | /**
83 | * Add the correct font weight in Chrome, Edge, and Safari.
84 | */
85 | b,
86 | strong {
87 | font-weight: bolder; }
88 |
89 | /**
90 | * 1. Correct the inheritance and scaling of font size in all browsers.
91 | * 2. Correct the odd `em` font sizing in all browsers.
92 | */
93 | code,
94 | kbd,
95 | samp {
96 | font-family: monospace, monospace;
97 | /* 1 */
98 | font-size: 1em;
99 | /* 2 */ }
100 |
101 | /**
102 | * Add the correct font size in all browsers.
103 | */
104 | small {
105 | font-size: 80%; }
106 |
107 | /**
108 | * Prevent `sub` and `sup` elements from affecting the line height in
109 | * all browsers.
110 | */
111 | sub,
112 | sup {
113 | font-size: 75%;
114 | line-height: 0;
115 | position: relative;
116 | vertical-align: baseline; }
117 |
118 | sub {
119 | bottom: -0.25em; }
120 |
121 | sup {
122 | top: -0.5em; }
123 |
124 | /* Embedded content
125 | ========================================================================== */
126 | /**
127 | * Remove the border on images inside links in IE 10.
128 | */
129 | img {
130 | border-style: none; }
131 |
132 | /* Forms
133 | ========================================================================== */
134 | /**
135 | * 1. Change the font styles in all browsers.
136 | * 2. Remove the margin in Firefox and Safari.
137 | */
138 | button,
139 | input,
140 | optgroup,
141 | select,
142 | textarea {
143 | font-family: inherit;
144 | /* 1 */
145 | font-size: 100%;
146 | /* 1 */
147 | line-height: 1.15;
148 | /* 1 */
149 | margin: 0;
150 | /* 2 */ }
151 |
152 | /**
153 | * Show the overflow in IE.
154 | * 1. Show the overflow in Edge.
155 | */
156 | button,
157 | input {
158 | /* 1 */
159 | overflow: visible; }
160 |
161 | /**
162 | * Remove the inheritance of text transform in Edge, Firefox, and IE.
163 | * 1. Remove the inheritance of text transform in Firefox.
164 | */
165 | button,
166 | select {
167 | /* 1 */
168 | text-transform: none; }
169 |
170 | /**
171 | * Correct the inability to style clickable types in iOS and Safari.
172 | */
173 | button,
174 | [type="button"],
175 | [type="reset"],
176 | [type="submit"] {
177 | -webkit-appearance: button; }
178 |
179 | /**
180 | * Remove the inner border and padding in Firefox.
181 | */
182 | button::-moz-focus-inner,
183 | [type="button"]::-moz-focus-inner,
184 | [type="reset"]::-moz-focus-inner,
185 | [type="submit"]::-moz-focus-inner {
186 | border-style: none;
187 | padding: 0; }
188 |
189 | /**
190 | * Restore the focus styles unset by the previous rule.
191 | */
192 | button:-moz-focusring,
193 | [type="button"]:-moz-focusring,
194 | [type="reset"]:-moz-focusring,
195 | [type="submit"]:-moz-focusring {
196 | outline: 1px dotted ButtonText; }
197 |
198 | /**
199 | * Correct the padding in Firefox.
200 | */
201 | fieldset {
202 | padding: 0.35em 0.75em 0.625em; }
203 |
204 | /**
205 | * 1. Correct the text wrapping in Edge and IE.
206 | * 2. Correct the color inheritance from `fieldset` elements in IE.
207 | * 3. Remove the padding so developers are not caught out when they zero out
208 | * `fieldset` elements in all browsers.
209 | */
210 | legend {
211 | box-sizing: border-box;
212 | /* 1 */
213 | color: inherit;
214 | /* 2 */
215 | display: table;
216 | /* 1 */
217 | max-width: 100%;
218 | /* 1 */
219 | padding: 0;
220 | /* 3 */
221 | white-space: normal;
222 | /* 1 */ }
223 |
224 | /**
225 | * Add the correct vertical alignment in Chrome, Firefox, and Opera.
226 | */
227 | progress {
228 | vertical-align: baseline; }
229 |
230 | /**
231 | * Remove the default vertical scrollbar in IE 10+.
232 | */
233 | textarea {
234 | overflow: auto; }
235 |
236 | /**
237 | * 1. Add the correct box sizing in IE 10.
238 | * 2. Remove the padding in IE 10.
239 | */
240 | [type="checkbox"],
241 | [type="radio"] {
242 | box-sizing: border-box;
243 | /* 1 */
244 | padding: 0;
245 | /* 2 */ }
246 |
247 | /**
248 | * Correct the cursor style of increment and decrement buttons in Chrome.
249 | */
250 | [type="number"]::-webkit-inner-spin-button,
251 | [type="number"]::-webkit-outer-spin-button {
252 | height: auto; }
253 |
254 | /**
255 | * 1. Correct the odd appearance in Chrome and Safari.
256 | * 2. Correct the outline style in Safari.
257 | */
258 | [type="search"] {
259 | -webkit-appearance: textfield;
260 | /* 1 */
261 | outline-offset: -2px;
262 | /* 2 */ }
263 |
264 | /**
265 | * Remove the inner padding in Chrome and Safari on macOS.
266 | */
267 | [type="search"]::-webkit-search-decoration {
268 | -webkit-appearance: none; }
269 |
270 | /**
271 | * 1. Correct the inability to style clickable types in iOS and Safari.
272 | * 2. Change font properties to `inherit` in Safari.
273 | */
274 | ::-webkit-file-upload-button {
275 | -webkit-appearance: button;
276 | /* 1 */
277 | font: inherit;
278 | /* 2 */ }
279 |
280 | /* Interactive
281 | ========================================================================== */
282 | /*
283 | * Add the correct display in Edge, IE 10+, and Firefox.
284 | */
285 | details {
286 | display: block; }
287 |
288 | /*
289 | * Add the correct display in all browsers.
290 | */
291 | summary {
292 | display: list-item; }
293 |
294 | /* Misc
295 | ========================================================================== */
296 | /**
297 | * Add the correct display in IE 10+.
298 | */
299 | template {
300 | display: none; }
301 |
302 | /**
303 | * Add the correct display in IE 10.
304 | */
305 | [hidden] {
306 | display: none; }
307 |
308 | :root {
309 | --border-width: 2px;
310 | --main-clr: #35ffce;
311 | --secondary-clr: #111;
312 | --background-clr: white;
313 | --text-clr-light: white;
314 | --text-clr-dark: #111;
315 | --btn-border-clr: var(--main-clr);
316 | --active-drag-clr: #f45;
317 | --checker-rect: rgba(0, 0, 0, 0.1); }
318 |
319 | .dark-theme {
320 | --main-clr: #111;
321 | --secondary-clr: #35ffce;
322 | --background-clr: rgb(34, 34, 34);
323 | --text-clr-light: #111;
324 | --text-clr-dark: white;
325 | --btn-border-clr: white;
326 | --checker-rect: rgba(255, 255, 255, 0.1); }
327 |
328 | *,
329 | *:before,
330 | *:after {
331 | box-sizing: border-box;
332 | -webkit-font-smoothing: antialiased;
333 | -moz-osx-font-smoothing: grayscale;
334 | text-rendering: optimizeLegibility; }
335 |
336 | *::-moz-selection {
337 | color: var(--secondary-clr);
338 | background: var(--main-clr); }
339 |
340 | *::selection {
341 | color: var(--secondary-clr);
342 | background: var(--main-clr); }
343 |
344 | body {
345 | overflow: hidden; }
346 |
347 | hr {
348 | border: none;
349 | border-top: 1px dashed var(--text-clr-light);
350 | opacity: 0.4;
351 | margin: 24px 0 10px; }
352 |
353 | #svg-element {
354 | overflow: visible;
355 | width: 100%;
356 | height: 100%; }
357 |
358 | #svg-container {
359 | position: relative;
360 | background-color: var(--background-clr); }
361 |
362 | #svg-control {
363 | width: 100%;
364 | height: 100%;
365 | overflow: visible;
366 | position: absolute; }
367 | #svg-control:hover #control-path {
368 | stroke: var(--active-drag-clr);
369 | opacity: 0.6; }
370 | #svg-control:hover .control-point {
371 | opacity: 1;
372 | fill: var(--active-drag-clr); }
373 | #svg-control.show {
374 | outline: 1px solid var(--checker-rect); }
375 |
376 | #scale-wrap {
377 | margin: auto; }
378 |
379 | #control-path {
380 | fill: none;
381 | stroke: var(--text-clr-dark);
382 | opacity: 0.15; }
383 |
384 | .control-point {
385 | opacity: 1;
386 | fill: var(--secondary-clr); }
387 |
388 | .dropzone-preview {
389 | z-index: 99999;
390 | position: absolute;
391 | pointer-events: none;
392 | width: 100%;
393 | height: 100%;
394 | opacity: 0;
395 | transition: all 0.2s ease; }
396 | .dropzone-preview .info {
397 | z-index: 99999;
398 | padding: 100px;
399 | border: var(--text-clr-dark) solid var(--border-width);
400 | transform: translate(-50%, -50%);
401 | background-color: var(--text-clr-light);
402 | position: absolute;
403 | top: 50%;
404 | left: 50%;
405 | display: flex;
406 | flex-direction: column;
407 | align-items: center; }
408 | .dropzone-preview .overlay {
409 | background: var(--main-clr);
410 | width: 100%;
411 | height: 100%;
412 | opacity: 0.8; }
413 | .dropzone-preview .caption {
414 | color: var(--text-clr-dark);
415 | line-height: 1.3; }
416 | .dropzone-preview .icon {
417 | display: inline-block;
418 | width: 230px;
419 | height: 176px;
420 | background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjMyIiBoZWlnaHQ9IjE3OCIgdmlld0JveD0iMCAwIDIzMiAxNzgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHg9IjEiIHk9IjEiIHdpZHRoPSIyMzAiIGhlaWdodD0iMTQyIiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtZGFzaGFycmF5PSI0IDQiLz4KPHJlY3QgeD0iMTE4IiB5PSI3MCIgd2lkdGg9Ijc2IiBoZWlnaHQ9Ijg3IiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLXdpZHRoPSIyIi8+CjxyZWN0IHg9IjExMiIgeT0iODciIHdpZHRoPSI2NyIgaGVpZ2h0PSIzNyIgZmlsbD0iIzUzRkZDMSIvPgo8cGF0aCBkPSJNMTI5Ljk2IDExMy4yNzZDMTI4LjYxMSAxMTMuMjc2IDEyNy40MzggMTEzLjA1NCAxMjYuNDQxIDExMi42MDlDMTI1LjQ0NSAxMTIuMTY0IDEyNC42NCAxMTEuNTU5IDEyNC4wMjYgMTEwLjc5MkwxMjYuMDI3IDEwOC41ODRDMTI2LjYxIDEwOS4yMjggMTI3LjI0NiAxMDkuNzAzIDEyNy45MzYgMTEwLjAxQzEyOC42NDIgMTEwLjMxNyAxMjkuMzU1IDExMC40NyAxMzAuMDc1IDExMC40N0MxMzAuOTAzIDExMC40NyAxMzEuNTMyIDExMC4yODYgMTMxLjk2MSAxMDkuOTE4QzEzMi4zOTEgMTA5LjU1IDEzMi42MDUgMTA5LjAyMSAxMzIuNjA1IDEwOC4zMzFDMTMyLjYwNSAxMDcuNzY0IDEzMi40MzcgMTA3LjMzNCAxMzIuMDk5IDEwNy4wNDNDMTMxLjc3NyAxMDYuNzUyIDEzMS4yMjUgMTA2LjU0NSAxMzAuNDQzIDEwNi40MjJMMTI4Ljc2NCAxMDYuMTQ2QzEyNy4zMjMgMTA1LjkwMSAxMjYuMjggMTA1LjM3MiAxMjUuNjM2IDEwNC41NTlDMTI0Ljk5MiAxMDMuNzMxIDEyNC42NyAxMDIuNzI3IDEyNC42NyAxMDEuNTQ2QzEyNC42NyA5OS45OTczIDEyNS4xNzYgOTguODAxMyAxMjYuMTg4IDk3Ljk1OEMxMjcuMiA5Ny4wOTkzIDEyOC42MTkgOTYuNjcgMTMwLjQ0MyA5Ni42N0MxMzEuNjg1IDk2LjY3IDEzMi43NjYgOTYuODYxNyAxMzMuNjg2IDk3LjI0NUMxMzQuNjA2IDk3LjYyODMgMTM1LjM1IDk4LjE0OTcgMTM1LjkxNyA5OC44MDlMMTMzLjk2MiAxMDAuOTk0QzEzMy41MzMgMTAwLjUxOSAxMzMuMDI3IDEwMC4xNTEgMTMyLjQ0NCA5OS44OUMxMzEuODYyIDk5LjYxNCAxMzEuMjAyIDk5LjQ3NiAxMzAuNDY2IDk5LjQ3NkMxMjguOTE4IDk5LjQ3NiAxMjguMTQzIDEwMC4xMTIgMTI4LjE0MyAxMDEuMzg1QzEyOC4xNDMgMTAxLjkzNyAxMjguMzEyIDEwMi4zNTEgMTI4LjY0OSAxMDIuNjI3QzEyOS4wMDIgMTAyLjkwMyAxMjkuNTY5IDEwMy4xMSAxMzAuMzUxIDEwMy4yNDhMMTMyLjAzIDEwMy41NDdDMTMzLjMzNCAxMDMuNzc3IDEzNC4zMzggMTA0LjI2OCAxMzUuMDQzIDEwNS4wMTlDMTM1Ljc0OSAxMDUuNzcgMTM2LjEwMSAxMDYuNzgyIDEzNi4xMDEgMTA4LjA1NUMxMzYuMTAxIDEwOC44MDYgMTM1Ljk3MSAxMDkuNTA0IDEzNS43MSAxMTAuMTQ4QzEzNS40NSAxMTAuNzc3IDEzNS4wNTkgMTExLjMyOSAxMzQuNTM3IDExMS44MDRDMTM0LjAxNiAxMTIuMjY0IDEzMy4zNzIgMTEyLjYyNCAxMzIuNjA1IDExMi44ODVDMTMxLjg1NCAxMTMuMTQ2IDEzMC45NzIgMTEzLjI3NiAxMjkuOTYgMTEzLjI3NlpNMTQxLjU2NiAxMTNMMTM3LjMxMSA5Ni45NDZIMTQxLjE1MkwxNDMuMDM4IDEwNS4xMTFMMTQzLjk4MSAxMDkuNzM0SDE0NC4xNjVMMTQ1LjEzMSAxMDUuMTExTDE0Ny4wNCA5Ni45NDZIMTUwLjY5N0wxNDYuNDQyIDExM0gxNDEuNTY2Wk0xNjAuMzQ5IDExMC43MjNIMTYwLjE0MkMxNjAuMDA0IDExMS4wNiAxNTkuODM1IDExMS4zODIgMTU5LjYzNiAxMTEuNjg5QzE1OS40MzYgMTExLjk5NiAxNTkuMTkxIDExMi4yNzIgMTU4LjkgMTEyLjUxN0MxNTguNjA4IDExMi43NDcgMTU4LjI2MyAxMTIuOTMxIDE1Ny44NjUgMTEzLjA2OUMxNTcuNDgxIDExMy4yMDcgMTU3LjA0NCAxMTMuMjc2IDE1Ni41NTQgMTEzLjI3NkMxNTUuNzcyIDExMy4yNzYgMTU1LjA3NCAxMTMuMDkyIDE1NC40NjEgMTEyLjcyNEMxNTMuODYzIDExMi4zNTYgMTUzLjM1NyAxMTEuODE5IDE1Mi45NDMgMTExLjExNEMxNTIuNTI5IDExMC40MDkgMTUyLjIxNCAxMDkuNTUgMTUyIDEwOC41MzhDMTUxLjggMTA3LjUyNiAxNTEuNzAxIDEwNi4zODQgMTUxLjcwMSAxMDUuMTExQzE1MS43MDEgMTAyLjM4MiAxNTIuMTk5IDEwMC4yOTYgMTUzLjE5NiA5OC44NTVDMTU0LjE5MiA5Ny4zOTgzIDE1NS43MzMgOTYuNjcgMTU3LjgxOSA5Ni42N0MxNTguNjQ3IDk2LjY3IDE1OS4zNzUgOTYuNzg1IDE2MC4wMDQgOTcuMDE1QzE2MC42MzIgOTcuMjI5NyAxNjEuMTY5IDk3LjU0NCAxNjEuNjE0IDk3Ljk1OEMxNjIuMDc0IDk4LjM1NjcgMTYyLjQ0OSA5OC44Mzk3IDE2Mi43NDEgOTkuNDA3QzE2My4wMzIgOTkuOTc0MyAxNjMuMjYyIDEwMC42MDMgMTYzLjQzMSAxMDEuMjkzTDE2MC4yMTEgMTAyLjA3NUMxNjAuMTE5IDEwMS43MzggMTYwLjAxMSAxMDEuNDE2IDE1OS44ODkgMTAxLjEwOUMxNTkuNzgxIDEwMC43ODcgMTU5LjYzNiAxMDAuNTAzIDE1OS40NTIgMTAwLjI1OEMxNTkuMjY4IDEwMC4wMTMgMTU5LjAzOCA5OS44MjEgMTU4Ljc2MiA5OS42ODNDMTU4LjQ4NiA5OS41Mjk3IDE1OC4xNDggOTkuNDUzIDE1Ny43NSA5OS40NTNDMTU2Ljg3NiA5OS40NTMgMTU2LjI1NSA5OS44MTMzIDE1NS44ODcgMTAwLjUzNEMxNTUuNTM0IDEwMS4yNTUgMTU1LjM1OCAxMDIuMjY3IDE1NS4zNTggMTAzLjU3VjEwNi4xOTJDMTU1LjM1OCAxMDcuNDk1IDE1NS41NDkgMTA4LjUzOCAxNTUuOTMzIDEwOS4zMkMxNTYuMzMxIDExMC4xMDIgMTU3LjAyOSAxMTAuNDkzIDE1OC4wMjYgMTEwLjQ5M0MxNTguODA4IDExMC40OTMgMTU5LjM5IDExMC4yNjMgMTU5Ljc3NCAxMDkuODAzQzE2MC4xNTcgMTA5LjM0MyAxNjAuMzQ5IDEwOC43NTMgMTYwLjM0OSAxMDguMDMyVjEwNi44NTlIMTU3LjYxMlYxMDQuNDIxSDE2My40MzFWMTEzSDE2MC4zNDlWMTEwLjcyM1oiIGZpbGw9ImJsYWNrIi8+CjxwYXRoIGQ9Ik0yMTIuNTUzIDE3Ni4xOTdIMTg2LjExOFYxNzAuMzk1TDE3My4zMjYgMTU5LjEzN0MxNzEuMzc3IDE1Ny40MjIgMTcwLjg3OSAxNTQuNTYyIDE3Mi4xMDEgMTUyLjI3MkMxNzMuNjk2IDE0OS4yODEgMTc3LjYwNSAxNDguMzc0IDE4MC4zMTYgMTUwLjQwOEwxNzkuODcyIDE0MC4yMDRDMTc5Ljc2IDEzNy42MzIgMTgxLjcyMiAxMzUuNDQyIDE4NC4yOSAxMzUuMjcxQzE4Ny4wMjQgMTM1LjA4OSAxODkuMzQyIDEzNy4yNTcgMTg5LjM0MiAxMzkuOTk4VjEzOC4xNThDMTg5LjM0MiAxMzUuMzA5IDE5MS42NTEgMTMzIDE5NC41IDEzM0MxOTcuMzQ5IDEzMyAxOTkuNjU4IDEzNS4zMDkgMTk5LjY1OCAxMzguMTU4VjEzOC44MDNDMTk5LjY1OCAxMzUuOTU0IDIwMS45NjcgMTMzLjY0NSAyMDQuODE2IDEzMy42NDVDMjA3LjY2NCAxMzMuNjQ1IDIwOS45NzQgMTM1Ljk1NCAyMDkuOTc0IDEzOC44MDNWMTQxLjM4MkMyMDkuOTc0IDEzOC44ODkgMjExLjk5NCAxMzYuODY4IDIxNC40ODcgMTM2Ljg2OEMyMTYuOTc5IDEzNi44NjggMjE5IDEzOC44ODkgMjE5IDE0MS4zODJWMTYwLjU4MkMyMTkgMTYzLjc4MyAyMTcuMDUxIDE2Ni42NjEgMjE0LjA4IDE2Ny44NUMyMTMuMTU3IDE2OC4yMTkgMjEyLjU1MyAxNjkuMTEyIDIxMi41NTMgMTcwLjEwNVYxNzYuMTk3WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTE4Ni4xMTggMTc2LjE5N0gyMTIuNTUzVjE3MC4xMDVDMjEyLjU1MyAxNjkuMTEyIDIxMy4xNTcgMTY4LjIxOSAyMTQuMDggMTY3Ljg1VjE2Ny44NUMyMTcuMDUxIDE2Ni42NjEgMjE5IDE2My43ODMgMjE5IDE2MC41ODJWMTQxLjM4MkMyMTkgMTM4Ljg4OSAyMTYuOTc5IDEzNi44NjggMjE0LjQ4NyAxMzYuODY4VjEzNi44NjhDMjExLjk5NCAxMzYuODY4IDIwOS45NzQgMTM4Ljg4OSAyMDkuOTc0IDE0MS4zODJWMTQzLjk2MVYxMzguODAzQzIwOS45NzQgMTM1Ljk1NCAyMDcuNjY0IDEzMy42NDUgMjA0LjgxNiAxMzMuNjQ1VjEzMy42NDVDMjAxLjk2NyAxMzMuNjQ1IDE5OS42NTggMTM1Ljk1NCAxOTkuNjU4IDEzOC44MDNWMTQyLjY3MVYxMzguMTU4QzE5OS42NTggMTM1LjMwOSAxOTcuMzQ5IDEzMyAxOTQuNSAxMzNWMTMzQzE5MS42NTEgMTMzIDE4OS4zNDIgMTM1LjMwOSAxODkuMzQyIDEzOC4xNThWMTQyLjY3MVYxMzkuOTk4QzE4OS4zNDIgMTM3LjI1NyAxODcuMDI0IDEzNS4wODkgMTg0LjI5IDEzNS4yNzFWMTM1LjI3MUMxODEuNzIyIDEzNS40NDIgMTc5Ljc2IDEzNy42MzIgMTc5Ljg3MiAxNDAuMjA0TDE4MC4zMTYgMTUwLjQwOFYxNTAuNDA4QzE3Ny42MDUgMTQ4LjM3NCAxNzMuNjk2IDE0OS4yODEgMTcyLjEwMSAxNTIuMjcyVjE1Mi4yNzJDMTcwLjg3OSAxNTQuNTYyIDE3MS4zNzcgMTU3LjQyMiAxNzMuMzI2IDE1OS4xMzdMMTg2LjExOCAxNzAuMzk1VjE3Ni4xOTdaIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utd2lkdGg9IjIiLz4KPHBhdGggZD0iTTE5MS45MjEgMTUxLjA1M1YxNjAuMDc5IiBzdHJva2U9ImJsYWNrIiBzdHJva2Utd2lkdGg9IjIiLz4KPHBhdGggZD0iTTE5OS42NTggMTUxLjA1M1YxNjAuMDc5IiBzdHJva2U9ImJsYWNrIiBzdHJva2Utd2lkdGg9IjIiLz4KPHBhdGggZD0iTTIwNy4zOTUgMTUxLjA1M1YxNjAuMDc5IiBzdHJva2U9ImJsYWNrIiBzdHJva2Utd2lkdGg9IjIiLz4KPC9zdmc+Cg==);
421 | background-size: cover;
422 | background-repeat: no-repeat; }
423 | .dropzone-preview.show {
424 | opacity: 1; }
425 |
426 | .actions {
427 | z-index: 9999;
428 | position: absolute;
429 | bottom: 20px;
430 | left: 20px;
431 | display: flex;
432 | flex-direction: column;
433 | max-width: 380px; }
434 | .actions a {
435 | color: var(--main-clr); }
436 |
437 | .content-block {
438 | cursor: default;
439 | background: var(--secondary-clr);
440 | padding: 24px;
441 | margin-bottom: 8px;
442 | color: var(--text-clr-light);
443 | font-size: 14px;
444 | overflow: hidden;
445 | position: relative; }
446 | .content-block ul {
447 | padding-left: 24px;
448 | margin: 0;
449 | list-style: none; }
450 | .content-block ul li:before {
451 | position: absolute;
452 | content: "\2022";
453 | color: var(--main-clr);
454 | font-weight: bold;
455 | display: inline-block;
456 | width: 1em;
457 | margin-left: -24px; }
458 | .content-block p {
459 | margin-top: 8px;
460 | margin-bottom: 16px;
461 | line-height: 1.4; }
462 | .content-block h3 {
463 | font-size: 14px;
464 | opacity: 0.4;
465 | margin-top: 0;
466 | margin-bottom: 20px;
467 | text-transform: uppercase; }
468 | .content-block.hide {
469 | opacity: 0;
470 | height: 0;
471 | padding: 0 24px;
472 | margin-bottom: 0; }
473 |
474 | .action-buttons {
475 | display: flex; }
476 |
477 | .close-btn {
478 | cursor: pointer;
479 | width: 30px;
480 | height: 30px;
481 | position: absolute;
482 | right: 12px;
483 | top: 8px;
484 | opacity: 0.4;
485 | transition: all 0.1s ease; }
486 | .close-btn:after {
487 | pointer-events: none;
488 | content: "";
489 | width: 16px;
490 | height: 3px;
491 | background: var(--text-clr-light);
492 | display: inline-block;
493 | position: absolute;
494 | top: 50%;
495 | left: 50%;
496 | transform: translate(-50%, -50%); }
497 | .close-btn:hover {
498 | opacity: 1; }
499 |
500 | #show-settings {
501 | margin-right: 8px; }
502 |
503 | #show-info {
504 | text-transform: lowercase;
505 | margin-right: 8px; }
506 |
507 | #save-result-btn {
508 | margin-right: 8px; }
509 |
510 | #load-svg-btn {
511 | margin-right: 8px; }
512 |
513 | #load-svg-input {
514 | display: none; }
515 |
516 | .range-selector {
517 | margin-bottom: 32px; }
518 | .range-selector input {
519 | width: 100%;
520 | margin-top: 20px; }
521 | .range-selector input[type="range"] {
522 | width: 100%;
523 | background-color: transparent;
524 | -webkit-appearance: none; }
525 | .range-selector input[type="range"]:focus {
526 | outline: none; }
527 | .range-selector input[type="range"]::-webkit-slider-runnable-track {
528 | background: var(--main-clr);
529 | border: 0.2px solid rgba(0, 1, 1, 0);
530 | width: 100%;
531 | height: 4px;
532 | cursor: pointer; }
533 | .range-selector input[type="range"]::-webkit-slider-thumb {
534 | margin-top: -8px;
535 | width: 32px;
536 | height: 18px;
537 | background: #ffffff;
538 | border-radius: 0;
539 | border: var(--border-width) solid #000000;
540 | cursor: pointer;
541 | -webkit-appearance: none; }
542 | .range-selector input[type="range"]:focus::-webkit-slider-runnable-track {
543 | background: var(--main-clr); }
544 | .range-selector input[type="range"]::-moz-range-track {
545 | background: var(--main-clr);
546 | border: 0.2px solid rgba(0, 1, 1, 0);
547 | width: 100%;
548 | height: 4px;
549 | cursor: pointer; }
550 | .range-selector input[type="range"]::-moz-range-thumb {
551 | width: 32px;
552 | height: 18px;
553 | background: #ffffff;
554 | border-radius: 0;
555 | border: var(--border-width) solid #000000;
556 | cursor: pointer; }
557 | .range-selector input[type="range"]::-ms-track {
558 | background: transparent;
559 | border-color: transparent;
560 | border-width: 5px 0;
561 | color: transparent;
562 | width: 100%;
563 | height: 4px;
564 | cursor: pointer; }
565 | .range-selector input[type="range"]::-ms-fill-lower {
566 | background: var(--main-clr);
567 | border: 0.2px solid rgba(0, 1, 1, 0); }
568 | .range-selector input[type="range"]::-ms-fill-upper {
569 | background: var(--main-clr);
570 | border: 0.2px solid rgba(0, 1, 1, 0); }
571 | .range-selector input[type="range"]::-ms-thumb {
572 | width: 32px;
573 | height: 18px;
574 | background: #ffffff;
575 | border-radius: 0;
576 | border: var(--border-width) solid #000000;
577 | cursor: pointer;
578 | margin-top: 0px; }
579 | .range-selector input[type="range"]:focus::-ms-fill-lower {
580 | background: var(--main-clr); }
581 | .range-selector input[type="range"]:focus::-ms-fill-upper {
582 | background: var(--main-clr); }
583 |
584 | .checkbox-block {
585 | display: flex;
586 | flex-direction: column;
587 | padding: 0 0 10px; }
588 | .checkbox-block input[type="checkbox"] {
589 | display: none; }
590 | .checkbox-block input[type="checkbox"] + label {
591 | display: flex;
592 | align-items: center;
593 | position: relative;
594 | cursor: pointer;
595 | padding: 0; }
596 | .checkbox-block input[type="checkbox"] + label:before {
597 | content: "";
598 | position: relative;
599 | margin-right: 18px;
600 | display: inline-block;
601 | width: 24px;
602 | height: 24px;
603 | background: white;
604 | border: var(--border-width) solid var(--text-clr-light); }
605 | .checkbox-block input[type="checkbox"]:checked + label:before {
606 | background: var(--main-clr); }
607 | .checkbox-block input[type="checkbox"]:checked + label:after {
608 | content: "";
609 | width: 20px;
610 | height: var(--border-width);
611 | background: var(--secondary-clr);
612 | display: inline-block;
613 | position: absolute;
614 | bottom: 11px;
615 | left: 2px;
616 | transform: rotate(-45deg); }
617 |
618 | .switcher {
619 | z-index: 999;
620 | position: absolute;
621 | top: 16px;
622 | right: 16px;
623 | -webkit-user-select: none;
624 | -moz-user-select: none;
625 | -ms-user-select: none;
626 | user-select: none; }
627 | .switcher input[type="checkbox"] {
628 | display: none; }
629 | .switcher input[type="checkbox"] + label {
630 | text-transform: uppercase;
631 | display: flex;
632 | align-items: center;
633 | position: relative;
634 | cursor: pointer;
635 | padding: 0;
636 | color: var(--secondary-clr); }
637 | .switcher input[type="checkbox"] + label:after {
638 | content: "";
639 | position: absolute;
640 | top: 50%;
641 | transform: translateY(-50%);
642 | left: -42px;
643 | display: inline-block;
644 | width: 14px;
645 | height: 20px;
646 | background: white;
647 | border: var(--border-width) solid var(--secondary-clr);
648 | transition: all 0.08s ease-in-out; }
649 | .switcher input[type="checkbox"] + label:before {
650 | content: "";
651 | position: absolute;
652 | left: -42px;
653 | margin-right: 10px;
654 | display: inline-block;
655 | width: 32px;
656 | height: 20px;
657 | background: var(--main-clr);
658 | border: var(--border-width) solid var(--secondary-clr); }
659 | .switcher input[type="checkbox"]:checked + label:after {
660 | left: -24px; }
661 |
662 | .primary-btn {
663 | cursor: pointer;
664 | -webkit-user-select: none;
665 | -moz-user-select: none;
666 | -ms-user-select: none;
667 | user-select: none;
668 | font-family: "IBM Plex Mono", monospace;
669 | font-size: 15px;
670 | text-transform: uppercase;
671 | padding: 0 16px;
672 | height: 54px;
673 | background: white;
674 | color: var(--secondary-clr);
675 | transition: all 0.05s ease-in-out;
676 | border: var(--border-width) solid var(--secondary-clr);
677 | color: #111; }
678 | .primary-btn:focus {
679 | outline: none; }
680 | .primary-btn:hover {
681 | background: var(--secondary-clr) !important;
682 | color: var(--main-clr) !important; }
683 | .primary-btn:hover svg {
684 | fill: var(--main-clr) !important; }
685 | .primary-btn.active {
686 | background: var(--main-clr);
687 | border: var(--border-width) solid var(--main-clr);
688 | color: var(--text-clr-dark); }
689 | .primary-btn.active:hover {
690 | background: var(--main-clr) !important;
691 | border: var(--border-width) solid var(--text-clr-dark) !important;
692 | color: var(--secondary-clr) !important; }
693 |
694 | .secondary-btn {
695 | cursor: pointer;
696 | -webkit-user-select: none;
697 | -moz-user-select: none;
698 | -ms-user-select: none;
699 | user-select: none;
700 | font-family: "IBM Plex Mono", monospace;
701 | font-size: 15px;
702 | text-transform: uppercase;
703 | padding: 0 16px;
704 | height: 54px;
705 | background: white;
706 | color: var(--secondary-clr);
707 | transition: all 0.05s ease-in-out;
708 | border: var(--border-width) solid var(--main-clr);
709 | color: #111; }
710 | .secondary-btn:focus {
711 | outline: none; }
712 | .secondary-btn:hover {
713 | background: var(--secondary-clr) !important;
714 | color: var(--main-clr) !important; }
715 | .secondary-btn:hover svg {
716 | fill: var(--main-clr) !important; }
717 | .secondary-btn.active {
718 | background: var(--main-clr);
719 | border: var(--border-width) solid var(--main-clr);
720 | color: var(--text-clr-dark); }
721 | .secondary-btn.active > svg {
722 | fill: var(--text-clr-dark); }
723 | .secondary-btn.active:hover {
724 | background: var(--main-clr) !important;
725 | border: var(--border-width) solid var(--text-clr-dark) !important;
726 | color: var(--secondary-clr) !important; }
727 | .secondary-btn.active:hover > svg {
728 | fill: var(--text-clr-dark) !important; }
729 |
730 | #loader {
731 | z-index: 999999;
732 | position: absolute;
733 | width: 100vw;
734 | height: 100vh;
735 | background: white;
736 | display: flex; }
737 |
738 | #loader-spinner {
739 | display: inline-block;
740 | width: 60px;
741 | height: 60px;
742 | background-color: var(--secondary-clr);
743 | margin: auto;
744 | -webkit-animation-name: transform-animation;
745 | animation-name: transform-animation;
746 | -webkit-animation-duration: 0.5s;
747 | animation-duration: 0.5s;
748 | -webkit-animation-iteration-count: infinite;
749 | animation-iteration-count: infinite;
750 | -webkit-animation-direction: alternate;
751 | animation-direction: alternate; }
752 |
753 | @-webkit-keyframes transform-animation {
754 | from {
755 | background-color: var(--secondary-clr);
756 | transform: scale(0.1) rotate(0deg); }
757 | to {
758 | background-color: var(--main-clr);
759 | transform: scale(0.9) rotate(45deg);
760 | border-radius: 100%; } }
761 |
762 | @keyframes transform-animation {
763 | from {
764 | background-color: var(--secondary-clr);
765 | transform: scale(0.1) rotate(0deg); }
766 | to {
767 | background-color: var(--main-clr);
768 | transform: scale(0.9) rotate(45deg);
769 | border-radius: 100%; } }
770 |
771 | #if-mobile {
772 | position: absolute;
773 | top: 50%;
774 | left: 50%;
775 | transform: translate(-50%, -50%);
776 | z-index: 99999;
777 | width: 90%;
778 | height: 90%;
779 | background: white;
780 | border: 8px solid var(--main-clr); }
781 | #if-mobile .icon {
782 | width: 100%;
783 | height: 100%;
784 | background-size: 60%;
785 | background-position: center;
786 | background-image: url(../fonts/text-for-mobile.4d297d.svg); }
787 |
788 | #app {
789 | display: flex;
790 | flex-direction: column;
791 | height: 100vh;
792 | width: 100%;
793 | font-family: "IBM Plex Mono", monospace;
794 | background-color: var(--background-clr); }
795 |
796 | .links {
797 | display: flex;
798 | flex-wrap: wrap;
799 | padding: 16px 0 8px; }
800 |
801 | .social-media {
802 | display: inline-block;
803 | margin-right: 20px;
804 | display: flex;
805 | align-items: center;
806 | text-decoration: none; }
807 | .social-media span {
808 | color: var(--main-clr);
809 | margin: 0 8px;
810 | font-size: 14px;
811 | transition: all 0.1s ease; }
812 | .social-media svg {
813 | fill: var(--main-clr);
814 | transform: scale(0.8);
815 | transition: all 0.1s ease; }
816 | .social-media:hover span {
817 | color: white; }
818 | .social-media:hover svg {
819 | fill: white; }
820 |
821 | .checkerboard-pattern {
822 | background-image: linear-gradient(45deg, var(--checker-rect) 25%, transparent 25%), linear-gradient(-45deg, var(--checker-rect) 25%, transparent 25%), linear-gradient(45deg, transparent 75%, var(--checker-rect) 75%), linear-gradient(-45deg, transparent 75%, var(--checker-rect) 75%) !important;
823 | background-size: 20px 20px;
824 | background-position: 0 0, 0 10px, 10px -10px, -10px 0px; }
825 |
826 |
--------------------------------------------------------------------------------
/dist/fonts/text-for-mobile.4d297d.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dist/images/dragndrop-icon.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/dist/images/favicons/favico-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PavelLaptev/warp-svg/056fb1cc736f2d9e29257602fcf4c37d300053e0/dist/images/favicons/favico-128.png
--------------------------------------------------------------------------------
/dist/images/favicons/favico-152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PavelLaptev/warp-svg/056fb1cc736f2d9e29257602fcf4c37d300053e0/dist/images/favicons/favico-152.png
--------------------------------------------------------------------------------
/dist/images/favicons/favico-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PavelLaptev/warp-svg/056fb1cc736f2d9e29257602fcf4c37d300053e0/dist/images/favicons/favico-16.png
--------------------------------------------------------------------------------
/dist/images/favicons/favico-167.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PavelLaptev/warp-svg/056fb1cc736f2d9e29257602fcf4c37d300053e0/dist/images/favicons/favico-167.png
--------------------------------------------------------------------------------
/dist/images/favicons/favico-180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PavelLaptev/warp-svg/056fb1cc736f2d9e29257602fcf4c37d300053e0/dist/images/favicons/favico-180.png
--------------------------------------------------------------------------------
/dist/images/favicons/favico-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PavelLaptev/warp-svg/056fb1cc736f2d9e29257602fcf4c37d300053e0/dist/images/favicons/favico-192.png
--------------------------------------------------------------------------------
/dist/images/favicons/favico-196.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PavelLaptev/warp-svg/056fb1cc736f2d9e29257602fcf4c37d300053e0/dist/images/favicons/favico-196.png
--------------------------------------------------------------------------------
/dist/images/favicons/favico-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PavelLaptev/warp-svg/056fb1cc736f2d9e29257602fcf4c37d300053e0/dist/images/favicons/favico-32.png
--------------------------------------------------------------------------------
/dist/images/text-for-mobile.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/dist/images/web-preview.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PavelLaptev/warp-svg/056fb1cc736f2d9e29257602fcf4c37d300053e0/dist/images/web-preview.jpg
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
15 |
16 |
17 |
18 |
19 | Warp SVG Online
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
32 |
33 |
34 |
35 |
36 |
37 |
40 |
43 |
44 |
45 |
46 |
47 |
Drop SVG file here
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
How To Use:
61 |
62 | -
63 |
Use this tutorial to avoid artifacts
66 |
67 | -
68 |
Drag'n'Drop to upload your SVG File
69 |
70 | -
71 |
Hold 'Space' and pan to move around the canvas
72 |
73 | -
74 |
Scroll to zoom
75 |
76 |
77 |
78 |
101 |
102 |
103 |
104 |
105 |
Settings:
106 |
107 |
108 |
109 |
110 |
111 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
138 |
139 |
140 |
141 |
142 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
160 |
161 |
162 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "warp-svg-online",
3 | "version": "1.0.1",
4 | "description": "Warp and distort SVG online",
5 | "scripts": {
6 | "build": "webpack --mode=development",
7 | "watch": "webpack --mode=development --watch",
8 | "watch:externalServer": "webpack --mode=development --watch --externalServer",
9 | "bundle": "npm ci && npm run watch",
10 | "bundle:externalServer": "npm ci && npm run watch:externalServer",
11 | "production": "cross-env NODE_ENV=production webpack --mode=production",
12 | "lint-sass": "sass-lint -v -q --format=compact",
13 | "lint-js": "eslint --ext .js src/js/"
14 | },
15 | "keywords": [
16 | "webpack",
17 | "boilerplate",
18 | "template",
19 | "setup"
20 | ],
21 | "author": "Pavel Laptev",
22 | "license": "MIT",
23 | "engines": {
24 | "node": ">=10.13.0"
25 | },
26 | "dependencies": {
27 | "autoprefixer": "^9.8.4",
28 | "calculate-aspect-ratio": "0.1.3",
29 | "gsap": "3.3.3",
30 | "warpjs": "1.0.8"
31 | },
32 | "devDependencies": {
33 | "@babel/core": "^7.10.3",
34 | "@babel/preset-env": "^7.10.3",
35 | "ajv": "^6.12.2",
36 | "babel-loader": "^8.1.0",
37 | "browser-sync": "^2.26.7",
38 | "browser-sync-webpack-plugin": "2.2.2",
39 | "clean-webpack-plugin": "^3.0.0",
40 | "copy-webpack-plugin": "^5.1.1",
41 | "cross-env": "^7.0.2",
42 | "css-loader": "^3.6.0",
43 | "cssnano": "^4.1.10",
44 | "eslint": "^6.8.0",
45 | "eslint-config-airbnb": "^18.2.0",
46 | "eslint-plugin-import": "^2.21.2",
47 | "eslint-plugin-jsx-a11y": "^6.3.1",
48 | "eslint-plugin-react": "^7.20.0",
49 | "eslint-plugin-react-hooks": "^1.7.0",
50 | "fibers": "^4.0.3",
51 | "file-loader": "^5.1.0",
52 | "html-webpack-plugin": "^3.2.0",
53 | "imagemin-webpack-plugin": "^2.4.2",
54 | "mini-css-extract-plugin": "^0.9.0",
55 | "node-sass": "^4.14.1",
56 | "optimize-css-assets-webpack-plugin": "^5.0.3",
57 | "postcss-loader": "^3.0.0",
58 | "sass": "^1.26.9",
59 | "sass-lint": "^1.13.1",
60 | "sass-loader": "^8.0.2",
61 | "style-loader": "^1.2.1",
62 | "terser-webpack-plugin": "^2.3.7",
63 | "url-loader": "^3.0.0",
64 | "webpack": "^4.43.0",
65 | "webpack-cli": "^3.3.12"
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: [
3 | require('autoprefixer'),
4 | ],
5 | };
6 |
--------------------------------------------------------------------------------
/src/images/dragndrop-icon.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/images/favicons/favico-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PavelLaptev/warp-svg/056fb1cc736f2d9e29257602fcf4c37d300053e0/src/images/favicons/favico-128.png
--------------------------------------------------------------------------------
/src/images/favicons/favico-152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PavelLaptev/warp-svg/056fb1cc736f2d9e29257602fcf4c37d300053e0/src/images/favicons/favico-152.png
--------------------------------------------------------------------------------
/src/images/favicons/favico-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PavelLaptev/warp-svg/056fb1cc736f2d9e29257602fcf4c37d300053e0/src/images/favicons/favico-16.png
--------------------------------------------------------------------------------
/src/images/favicons/favico-167.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PavelLaptev/warp-svg/056fb1cc736f2d9e29257602fcf4c37d300053e0/src/images/favicons/favico-167.png
--------------------------------------------------------------------------------
/src/images/favicons/favico-180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PavelLaptev/warp-svg/056fb1cc736f2d9e29257602fcf4c37d300053e0/src/images/favicons/favico-180.png
--------------------------------------------------------------------------------
/src/images/favicons/favico-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PavelLaptev/warp-svg/056fb1cc736f2d9e29257602fcf4c37d300053e0/src/images/favicons/favico-192.png
--------------------------------------------------------------------------------
/src/images/favicons/favico-196.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PavelLaptev/warp-svg/056fb1cc736f2d9e29257602fcf4c37d300053e0/src/images/favicons/favico-196.png
--------------------------------------------------------------------------------
/src/images/favicons/favico-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PavelLaptev/warp-svg/056fb1cc736f2d9e29257602fcf4c37d300053e0/src/images/favicons/favico-32.png
--------------------------------------------------------------------------------
/src/images/text-for-mobile.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/images/web-preview.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PavelLaptev/warp-svg/056fb1cc736f2d9e29257602fcf4c37d300053e0/src/images/web-preview.jpg
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
15 |
16 |
17 |
18 |
19 | Warp SVG Online
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
32 |
33 |
34 |
35 |
36 |
37 |
40 |
43 |
44 |
45 |
46 |
47 |
Drop SVG file here
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
How To Use:
61 |
62 | -
63 |
Use this tutorial to avoid artifacts
66 |
67 | -
68 |
Drag'n'Drop to upload your SVG File
69 |
70 | -
71 |
Hold 'Space' and pan to move around the canvas
72 |
73 | -
74 |
Scroll to zoom
75 |
76 |
77 |
78 |
101 |
102 |
103 |
104 |
105 |
Settings:
106 |
107 |
108 |
109 |
110 |
111 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
138 |
139 |
140 |
141 |
142 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
160 |
161 |
162 |
--------------------------------------------------------------------------------
/src/js/chunks/changeTheme.js:
--------------------------------------------------------------------------------
1 | const changeTheme = () => {
2 | const app = document.getElementById('app');
3 | const swither = document.getElementById('switch-theme');
4 |
5 | swither.addEventListener('change', (e) => {
6 | app.classList.toggle('dark-theme');
7 | });
8 | };
9 |
10 | export default changeTheme;
11 |
--------------------------------------------------------------------------------
/src/js/chunks/downloadAsFile.js:
--------------------------------------------------------------------------------
1 | const downloadAsFile = (content, filename, contentType) => {
2 | if (!contentType) contentType = 'application/octet-stream';
3 | const a = document.createElement('a');
4 | const blob = new Blob([content], { type: contentType });
5 | a.href = window.URL.createObjectURL(blob);
6 | a.download = filename;
7 | a.click();
8 | };
9 |
10 | export default downloadAsFile;
11 |
--------------------------------------------------------------------------------
/src/js/chunks/dropzone.js:
--------------------------------------------------------------------------------
1 | const dropZone = (callback) => {
2 | const loadBtn = document.getElementById('load-svg-btn');
3 | const loadInput = document.getElementById('load-svg-input');
4 |
5 | const toggleDropZone = (e, hideZonePreview) => {
6 | e.stopPropagation();
7 | e.preventDefault();
8 | dropZonePreview.classList.toggle('show');
9 | // hideZonePreview(dropZonePreview);
10 | typeof hideZonePreview === 'function' && hideZonePreview(dropZonePreview);
11 | };
12 |
13 | const dropEvent = (e, file, hideZonePreview) => {
14 | toggleDropZone(e, hideZonePreview);
15 |
16 | const fileReader = new FileReader();
17 | fileReader.readAsText(file);
18 |
19 | // console.log(e.type);
20 | fileReader.onload = () => {
21 | try {
22 | callback(fileReader.result);
23 | } catch (err) {
24 | console.error(err);
25 | }
26 | };
27 | };
28 |
29 | const dropZonePreview = document.getElementsByClassName(
30 | 'dropzone-preview',
31 | )[0];
32 |
33 | const clickOnLoadInput = () => {
34 | loadInput.click();
35 | };
36 |
37 | window.addEventListener('dragenter', toggleDropZone);
38 | window.addEventListener('dragleave', toggleDropZone);
39 | window.addEventListener('dragover', (e) => e.preventDefault());
40 | window.addEventListener('dragstart', (e) => e.preventDefault());
41 | window.addEventListener('drageend', (e) => e.preventDefault());
42 | window.addEventListener('drop', (e) => dropEvent(e, e.dataTransfer.files[0]));
43 |
44 | loadBtn.addEventListener('click', clickOnLoadInput);
45 | loadInput.addEventListener('change', (e) => dropEvent(e, e.target.files[0], (zone) => { zone.classList.toggle('show'); }));
46 | };
47 |
48 | export default dropZone;
49 |
--------------------------------------------------------------------------------
/src/js/chunks/generateMeshPoints.js:
--------------------------------------------------------------------------------
1 | const generateMeshPoints = (width, height, amount) => {
2 | const checkAndRoundNumber = (length, index) => (length / amount) * index;
3 |
4 | const myArray = [...Array(amount).keys()];
5 |
6 | const myleft = myArray.map((item, i) => [0, checkAndRoundNumber(height, i)]);
7 |
8 | const myBottom = myArray.map((item, i) => [checkAndRoundNumber(width, i), height]);
9 |
10 | const myRight = myArray
11 | .map((item, i) => [width, checkAndRoundNumber(height, ++i)])
12 | .reverse();
13 |
14 | const myTop = [...Array(amount).keys()]
15 | .map((item, i) => [checkAndRoundNumber(width, ++i), 0])
16 | .reverse();
17 |
18 | return [...myleft, ...myBottom, ...myRight, ...myTop];
19 | };
20 |
21 | export default generateMeshPoints;
22 |
--------------------------------------------------------------------------------
/src/js/chunks/isTouch.js:
--------------------------------------------------------------------------------
1 | const isTouch = () => {
2 | if ((('ontouchstart' in window) || (navigator.MaxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0)) && window.screen.width <= 600) {
3 | console.log('touch event is true');
4 | return true;
5 | }
6 | console.log('touch event is false');
7 | return false;
8 | };
9 |
10 | export default isTouch;
11 |
--------------------------------------------------------------------------------
/src/js/chunks/iterpritateSmoothness.js:
--------------------------------------------------------------------------------
1 | const iterpritateSmoothness = (val) => {
2 | const newVal = Number(val);
3 | if (newVal === 0) {
4 | return 400;
5 | }
6 | if (newVal === 100) {
7 | return 200;
8 | }
9 | if (newVal === 200) {
10 | return 80;
11 | }
12 | if (newVal === 300) {
13 | return 30;
14 | }
15 | if (newVal === 400) {
16 | return 10;
17 | }
18 | return null;
19 | };
20 |
21 | export default iterpritateSmoothness;
22 |
--------------------------------------------------------------------------------
/src/js/chunks/loader.js:
--------------------------------------------------------------------------------
1 | const loader = () => {
2 | window.onload = function () {
3 | document.getElementById('app').style.opacity = 1;
4 | document.getElementById('loader').style.display = 'none';
5 | };
6 | };
7 |
8 | export default loader;
9 |
--------------------------------------------------------------------------------
/src/js/chunks/moveCanvas.js:
--------------------------------------------------------------------------------
1 | import Draggable from 'gsap/Draggable';
2 |
3 | const moveCanvas = (movingElement) => {
4 | const canvasDrag = Draggable.create(movingElement, {
5 | trigger: document.body,
6 | cursor: 'auto',
7 | });
8 |
9 | canvasDrag[0].disable();
10 |
11 | // "SPACE" KEY PRESSED
12 | document.addEventListener('keydown', function (e) {
13 | if (e.code === 'Space') {
14 | canvasDrag[0].enable();
15 | document.body.style.cursor = 'grab';
16 | }
17 | });
18 |
19 | document.addEventListener('keyup', function (e) {
20 | if (e.code === 'Space') {
21 | canvasDrag[0].disable();
22 | document.body.style.cursor = 'default';
23 | }
24 | });
25 | };
26 |
27 | export default moveCanvas;
28 |
--------------------------------------------------------------------------------
/src/js/chunks/saveResults.js:
--------------------------------------------------------------------------------
1 | import downloadAsFile from './downloadAsFile';
2 |
3 | const saveResult = (btn, content) => {
4 | btn.onclick = () => {
5 | downloadAsFile(
6 | new XMLSerializer().serializeToString(content),
7 | 'warped-svg.svg',
8 | );
9 | };
10 | };
11 |
12 | export default saveResult;
13 |
--------------------------------------------------------------------------------
/src/js/chunks/svg-test-string.js:
--------------------------------------------------------------------------------
1 | export const testSVG =
2 | '';
3 |
--------------------------------------------------------------------------------
/src/js/chunks/toggleControls.js:
--------------------------------------------------------------------------------
1 | const toggleControls = () => {
2 | const showInfoBtn = document.getElementById('show-info');
3 | const infoBlock = document.getElementById('info-block');
4 | const showSettingsBtn = document.getElementById('show-settings');
5 | const settingsBlock = document.getElementById('settings-block');
6 |
7 | showInfoBtn.addEventListener('click', (e) => {
8 | showInfoBtn.classList.toggle('active');
9 | infoBlock.classList.toggle('hide');
10 | });
11 |
12 | showSettingsBtn.addEventListener('click', (e) => {
13 | showSettingsBtn.classList.toggle('active');
14 | settingsBlock.classList.toggle('hide');
15 | });
16 |
17 | infoBlock
18 | .getElementsByClassName('close-btn')[0]
19 | .addEventListener('click', () => {
20 | showInfoBtn.classList.toggle('active');
21 | infoBlock.classList.toggle('hide');
22 | });
23 |
24 | settingsBlock
25 | .getElementsByClassName('close-btn')[0]
26 | .addEventListener('click', () => {
27 | showSettingsBtn.classList.toggle('active');
28 | settingsBlock.classList.toggle('hide');
29 | });
30 | };
31 |
32 | export default toggleControls;
33 |
--------------------------------------------------------------------------------
/src/js/index.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable quotes */
2 | import "../scss/styles.scss";
3 | import Warp from "warpjs";
4 | import gsap from "gsap";
5 | import Draggable from "gsap/Draggable";
6 |
7 | import dropZone from "./chunks/dropzone";
8 | import generateMeshPoints from "./chunks/generateMeshPoints";
9 | import saveResult from "./chunks/saveResults";
10 | import moveCanvas from "./chunks/moveCanvas";
11 | import toggleControls from "./chunks/toggleControls";
12 | import changeTheme from "./chunks/changeTheme";
13 | import iterpritateSmoothness from "./chunks/iterpritateSmoothness";
14 | import loader from "./chunks/loader";
15 |
16 | import { testSVG } from "./chunks/svg-test-string";
17 |
18 | /// /////////////////////////////////////////////////////////////////
19 | /// /////////// Register GSAP Draggable and Loader //////////////////
20 | /// /////////////////////////////////////////////////////////////////
21 | loader();
22 | gsap.registerPlugin(Draggable);
23 |
24 | /// /////////////////////////////////////////////////////////////////
25 | /// ///////////////////// Initial Variables /////////////////////////
26 | /// /////////////////////////////////////////////////////////////////
27 | let svgString = testSVG;
28 | let zoom = 1;
29 | const draggableControlPonts = [];
30 |
31 | const app = document.getElementById("app");
32 | const svgContainer = document.getElementById("svg-container");
33 | const svgElement = document.getElementById("svg-element");
34 | const svgControl = document.getElementById("svg-control");
35 | const zoomElement = document.getElementById("scale-wrap");
36 |
37 | const actions = {
38 | meshComplexity: document.getElementById("mesh-complexity"),
39 | meshInterpolation: document.getElementById("interpolation-complexity"),
40 | showOriginalBox: document.getElementById("show-original-box-btn"),
41 | };
42 |
43 | let width = svgContainer.clientWidth;
44 | let height = svgContainer.clientHeight;
45 | let complexityLevel = actions.meshComplexity.value;
46 | let interpolationLevel = iterpritateSmoothness(actions.meshInterpolation.value);
47 |
48 | /// /////////////////////////////////////////////////////////////////
49 | /// ///////////////////// Parse SVG String //////////////////////////
50 | /// /////////////////////////////////////////////////////////////////
51 | function parseSVGString(svgString) {
52 | const svgDOM = new DOMParser()
53 | .parseFromString(svgString, "image/svg+xml")
54 | .getElementsByTagName("svg")[0];
55 |
56 | const parsedSVGWidth = svgDOM.attributes.width
57 | ? Number(svgDOM.attributes.width.value.replace(/^.*?(\d+).*/, "$1"))
58 | : 500;
59 | const parsedSVGheight = svgDOM.attributes.height
60 | ? Number(svgDOM.attributes.height.value.replace(/^.*?(\d+).*/, "$1"))
61 | : 500;
62 |
63 | height = Math.round(parsedSVGheight);
64 | width = Math.round(parsedSVGWidth);
65 | svgContainer.style.height = `${Math.round(height)}px`;
66 | svgContainer.style.width = `${Math.round(width)}px`;
67 |
68 | svgDOM.attributes.viewBox
69 | ? svgElement.setAttribute("viewBox", svgDOM.attributes.viewBox.value)
70 | : false;
71 | svgDOM.attributes.fill
72 | ? svgElement.setAttribute("fill", svgDOM.attributes.fill.value)
73 | : svgElement.setAttribute("fill", "inherit");
74 |
75 | svgElement.setAttribute("preserveAspectRatio", "xMidYMin meet");
76 | svgElement.innerHTML = svgDOM.innerHTML.toString();
77 | }
78 |
79 | /// /////////////////////////////////////////////////////////////////
80 | /// ///////////////////// Initial function //////////////////////////
81 | /// /////////////////////////////////////////////////////////////////
82 | function init(firstInit = false) {
83 | const controlPath = document.getElementById("control-path");
84 | parseSVGString(svgString);
85 | zoomElement.style.transform = "scale(1)";
86 | zoom = 1;
87 |
88 | // Need to interpolate first, so angles remain sharp
89 | const warp = new Warp(svgElement);
90 | warp.interpolate(interpolationLevel);
91 |
92 | // Start with a rectangle, then distort it later
93 | let controlPoints = generateMeshPoints(
94 | width,
95 | height,
96 | Number(complexityLevel)
97 | );
98 |
99 | // Compute weights from control points
100 | warp.transform(function (v0, V = controlPoints) {
101 | const A = [];
102 | const W = [];
103 | const L = [];
104 |
105 | // Find angles
106 | for (let i = 0; i < V.length; i++) {
107 | const j = (i + 1) % V.length;
108 |
109 | const vi = V[i];
110 | const vj = V[j];
111 |
112 | const r0i = Math.sqrt((v0[0] - vi[0]) ** 2 + (v0[1] - vi[1]) ** 2);
113 | const r0j = Math.sqrt((v0[0] - vj[0]) ** 2 + (v0[1] - vj[1]) ** 2);
114 | const rij = Math.sqrt((vi[0] - vj[0]) ** 2 + (vi[1] - vj[1]) ** 2);
115 |
116 | const dn = 2 * r0i * r0j;
117 | const r = (r0i ** 2 + r0j ** 2 - rij ** 2) / dn;
118 |
119 | A[i] = isNaN(r) ? 0 : Math.acos(Math.max(-1, Math.min(r, 1)));
120 | }
121 |
122 | // Find weights
123 | for (let j = 0; j < V.length; j++) {
124 | const i = (j > 0 ? j : V.length) - 1;
125 |
126 | // const vi = V[i];
127 | const vj = V[j];
128 |
129 | const r = Math.sqrt((vj[0] - v0[0]) ** 2 + (vj[1] - v0[1]) ** 2);
130 |
131 | W[j] = (Math.tan(A[i] / 2) + Math.tan(A[j] / 2)) / r;
132 | }
133 |
134 | // Normalise weights
135 | const Ws = W.reduce((a, b) => a + b, 0);
136 | for (let i = 0; i < V.length; i++) {
137 | L[i] = W[i] / Ws;
138 | }
139 |
140 | // Save weights to the point for use when transforming
141 | return [...v0, ...L];
142 | });
143 |
144 | // Warp function
145 | function reposition([x, y, ...W], V = controlPoints) {
146 | let nx = 0;
147 | let ny = 0;
148 |
149 | // Recreate the points using mean value coordinates
150 | for (let i = 0; i < V.length; i++) {
151 | nx += W[i] * V[i][0];
152 | ny += W[i] * V[i][1];
153 | }
154 |
155 | return [nx, ny, ...W];
156 | }
157 |
158 | // Draw control shape
159 | function drawControlShape(element = controlPath, V = controlPoints) {
160 | const path = [`M${V[0][0]} ${V[0][1]}`];
161 |
162 | for (let i = 1; i < V.length; i++) {
163 | path.push(`L${V[i][0]} ${V[i][1]}`);
164 | }
165 |
166 | path.push("Z");
167 | element.setAttribute("d", path.join(""));
168 | }
169 |
170 | // Draw control point
171 | function drawPoint(element, pos = { x: 0, y: 0 }, index) {
172 | const point = document.createElementNS(
173 | "http://www.w3.org/2000/svg",
174 | "circle"
175 | );
176 | point.setAttributeNS(null, "class", "control-point");
177 | point.setAttributeNS(null, "cx", pos.x);
178 | point.setAttributeNS(null, "cy", pos.y);
179 | point.setAttributeNS(null, "r", 6);
180 | element.appendChild(point);
181 |
182 | draggableControlPonts.push(point);
183 |
184 | Draggable.create(point, {
185 | type: "x,y",
186 | onDrag: function () {
187 | const relativeX =
188 | (this.pointerX - svgControl.getBoundingClientRect().left) / zoom;
189 | const relativeY =
190 | (this.pointerY - svgControl.getBoundingClientRect().top) / zoom;
191 |
192 | controlPoints[index] = [relativeX, relativeY];
193 | drawControlShape();
194 | warp.transform(reposition);
195 | },
196 | });
197 | }
198 |
199 | // Place control points
200 | function drawControlPoints(element = svgControl, V = controlPoints) {
201 | V.map((i, index) => {
202 | drawPoint(element, { x: i[0], y: i[1] }, index);
203 | return null;
204 | });
205 | }
206 |
207 | // if this is the first launch
208 | if (firstInit) {
209 | controlPoints = [
210 | [-70, -5],
211 | [-2, 136],
212 | [-90, 200],
213 | [20, 380],
214 | [150, 260],
215 | [400, 400],
216 | [490, 250],
217 | [400, 90],
218 | [260, 6],
219 | [470, 80],
220 | [360, -40],
221 | [80, -90],
222 | ];
223 | }
224 | drawControlShape();
225 | drawControlPoints();
226 | warp.transform(reposition);
227 | }
228 |
229 | /// //////
230 | const createNewControlPath = () => {
231 | svgControl.innerHTML = "";
232 | const newControlPath = document.createElementNS(
233 | "http://www.w3.org/2000/svg",
234 | "path"
235 | );
236 | newControlPath.setAttributeNS(null, "id", "control-path");
237 | svgControl.appendChild(newControlPath);
238 | };
239 |
240 | /// //////
241 | dropZone((result) => {
242 | svgString = result;
243 | createNewControlPath();
244 | init();
245 | });
246 |
247 | /// ////
248 | document.addEventListener("wheel", function (e) {
249 | const controlPath = document.getElementById("control-path");
250 | if (e.deltaY > 0) {
251 | zoomElement.style.transform = `scale(${(zoom += 0.02)})`;
252 | controlPath.style.strokeWidth = `${1 / zoom}px`;
253 | // console.log(svgControl.querySelectorAll('circle'));
254 | } else if (zoomElement.getBoundingClientRect().width >= 30) {
255 | zoomElement.style.transform = `scale(${(zoom -= 0.02)})`;
256 | controlPath.style.strokeWidth = `${1 / zoom}px`;
257 | }
258 | draggableControlPonts.map((i) => {
259 | if (i.getBoundingClientRect().height > 6) {
260 | i.setAttribute("r", 6 / zoom);
261 | }
262 | });
263 | });
264 |
265 | /// //////
266 | actions.meshComplexity.addEventListener(
267 | "change",
268 | (e) => {
269 | complexityLevel = e.target.value;
270 | createNewControlPath();
271 | init();
272 | },
273 | false
274 | );
275 |
276 | /// /////
277 | actions.showOriginalBox.addEventListener(
278 | "change",
279 | () => {
280 | svgControl.classList.toggle("show");
281 | app.classList.toggle("checkerboard-pattern");
282 | },
283 | false
284 | );
285 |
286 | // /////
287 | actions.meshInterpolation.addEventListener(
288 | "change",
289 | (e) => {
290 | interpolationLevel = iterpritateSmoothness(e.target.value);
291 | createNewControlPath();
292 | init();
293 | },
294 | false
295 | );
296 |
297 | // Initial calling
298 | changeTheme();
299 | moveCanvas(svgContainer);
300 | saveResult(document.getElementById("save-result-btn"), svgElement);
301 | toggleControls();
302 | init(true);
303 | console.log(`
304 | ▄ ▄
305 | ▌▒█ ▄▀▒▌
306 | ▌▒▒█ ▄▀▒▒▒▐
307 | ▐▄▀▒▒▀▀▀▀▄▄▄▀▒▒▒▒▒▐
308 | ▄▄▀▒░▒▒▒▒▒▒▒▒▒█▒▒▄█▒▐
309 | ▄▀▒▒▒░░░▒▒▒░░░▒▒▒▀██▀▒▌
310 | ▐▒▒▒▄▄▒▒▒▒░░░▒▒▒▒▒▒▒▀▄▒▒▌
311 | ▌░░▌█▀▒▒▒▒▒▄▀█▄▒▒▒▒▒▒▒█▒▐
312 | ▐░░░▒▒▒▒▒▒▒▒▌██▀▒▒░░░▒▒▒▀▄▌
313 | ▌░▒▄██▄▒▒▒▒▒▒▒▒▒░░░░░░▒▒▒▒▌
314 | ▌▒▀▐▄█▄█▌▄░▀▒▒░░░░░░░░░░▒▒▒▐
315 | ▐▒▒▐▀▐▀▒░▄▄▒▄▒▒▒▒▒▒░▒░▒░▒▒▒▒▌
316 | ▐▒▒▒▀▀▄▄▒▒▒▄▒▒▒▒▒▒▒▒░▒░▒░▒▒▐
317 | ▌▒▒▒▒▒▒▀▀▀▒▒▒▒▒▒░▒░▒░▒░▒▒▒▌
318 | ▐▒▒▒▒▒▒▒▒▒▒▒▒▒▒░▒░▒░▒▒▄▒▒▐
319 | ▀▄▒▒▒▒▒▒▒▒▒▒▒░▒░▒░▒▄▒▒▒▒▌
320 | ▀▄▒▒▒▒▒▒▒▒▒▒▄▄▄▀▒▒▒▒▄▀
321 | ▀▄▄▄▄▄▄▀▀▀▒▒▒▒▒▄▄▀
322 | ▒▒▒▒▒▒▒▒▒▒▀▀
323 |
324 | ░░░░░░░ █░█ ▄▀█ █░░ █░░ █▀█ ░ ░░░░░░░
325 | ░░░░░░░ █▀█ █▀█ █▄▄ █▄▄ █▄█ █ ░░░░░░░
326 | `);
327 |
--------------------------------------------------------------------------------
/src/scss/_base.scss:
--------------------------------------------------------------------------------
1 | *,
2 | *:before,
3 | *:after {
4 | box-sizing: border-box;
5 | -webkit-font-smoothing: antialiased;
6 | -moz-osx-font-smoothing: grayscale;
7 | text-rendering: optimizeLegibility;
8 | }
9 |
10 | *::selection {
11 | color: var(--secondary-clr);
12 | background: var(--main-clr);
13 | }
14 |
15 | body {
16 | overflow: hidden;
17 | }
18 |
19 | hr {
20 | border: none;
21 | border-top: 1px dashed var(--text-clr-light);
22 | opacity: 0.4;
23 | margin: 24px 0 10px;
24 | }
25 |
--------------------------------------------------------------------------------
/src/scss/_controls.scss:
--------------------------------------------------------------------------------
1 | //
2 | .actions {
3 | // user-select: none;
4 | z-index: 9999;
5 | position: absolute;
6 | bottom: 20px;
7 | left: 20px;
8 | display: flex;
9 | flex-direction: column;
10 | max-width: 380px;
11 | a {
12 | color: var(--main-clr);
13 | }
14 | }
15 |
16 | .content-block {
17 | @include content-block;
18 | }
19 |
20 | .action-buttons {
21 | display: flex;
22 | }
23 |
24 | .close-btn {
25 | cursor: pointer;
26 | width: 30px;
27 | height: 30px;
28 | position: absolute;
29 | right: 12px;
30 | top: 8px;
31 | opacity: 0.4;
32 | transition: all 0.1s ease;
33 | &:after {
34 | pointer-events: none;
35 | content: "";
36 | width: 16px;
37 | height: 3px;
38 | background: var(--text-clr-light);
39 | display: inline-block;
40 | position: absolute;
41 | top: 50%;
42 | left: 50%;
43 | transform: translate(-50%, -50%);
44 | }
45 | &:hover {
46 | opacity: 1;
47 | }
48 | }
49 |
50 | #show-settings {
51 | margin-right: 8px;
52 | }
53 |
54 | #show-info {
55 | text-transform: lowercase;
56 | margin-right: 8px;
57 | }
58 |
59 | #save-result-btn {
60 | margin-right: 8px;
61 | }
62 |
63 | #load-svg-btn {
64 | margin-right: 8px;
65 | }
66 |
67 | #load-svg-input {
68 | display: none;
69 | }
70 |
71 | .range-selector {
72 | margin-bottom: 32px;
73 | input {
74 | width: 100%;
75 | margin-top: 20px;
76 | }
77 | input[type="range"] {
78 | width: 100%;
79 | background-color: transparent;
80 | -webkit-appearance: none;
81 | }
82 | input[type="range"]:focus {
83 | outline: none;
84 | }
85 | input[type="range"]::-webkit-slider-runnable-track {
86 | background: var(--main-clr);
87 | border: 0.2px solid rgba(0, 1, 1, 0);
88 | width: 100%;
89 | height: 4px;
90 | cursor: pointer;
91 | }
92 | input[type="range"]::-webkit-slider-thumb {
93 | margin-top: -8px;
94 | width: 32px;
95 | height: 18px;
96 | background: #ffffff;
97 | border-radius: 0;
98 | border: var(--border-width) solid #000000;
99 | cursor: pointer;
100 | -webkit-appearance: none;
101 | }
102 | input[type="range"]:focus::-webkit-slider-runnable-track {
103 | background: var(--main-clr);
104 | }
105 | input[type="range"]::-moz-range-track {
106 | background: var(--main-clr);
107 | border: 0.2px solid rgba(0, 1, 1, 0);
108 | width: 100%;
109 | height: 4px;
110 | cursor: pointer;
111 | }
112 | input[type="range"]::-moz-range-thumb {
113 | width: 32px;
114 | height: 18px;
115 | background: #ffffff;
116 | border-radius: 0;
117 | border: var(--border-width) solid #000000;
118 | cursor: pointer;
119 | }
120 | input[type="range"]::-ms-track {
121 | background: transparent;
122 | border-color: transparent;
123 | border-width: 5px 0;
124 | color: transparent;
125 | width: 100%;
126 | height: 4px;
127 | cursor: pointer;
128 | }
129 | input[type="range"]::-ms-fill-lower {
130 | background: var(--main-clr);
131 | border: 0.2px solid rgba(0, 1, 1, 0);
132 | }
133 | input[type="range"]::-ms-fill-upper {
134 | background: var(--main-clr);
135 | border: 0.2px solid rgba(0, 1, 1, 0);
136 | }
137 | input[type="range"]::-ms-thumb {
138 | width: 32px;
139 | height: 18px;
140 | background: #ffffff;
141 | border-radius: 0;
142 | border: var(--border-width) solid #000000;
143 | cursor: pointer;
144 | margin-top: 0px;
145 | }
146 | input[type="range"]:focus::-ms-fill-lower {
147 | background: var(--main-clr);
148 | }
149 | input[type="range"]:focus::-ms-fill-upper {
150 | background: var(--main-clr);
151 | }
152 | }
153 |
154 | .checkbox-block {
155 | display: flex;
156 | flex-direction: column;
157 | padding: 0 0 10px;
158 | input[type="checkbox"] {
159 | display: none;
160 | & + label {
161 | display: flex;
162 | align-items: center;
163 | position: relative;
164 | cursor: pointer;
165 | padding: 0;
166 | }
167 | & + label:before {
168 | content: "";
169 | position: relative;
170 | // margin-top: 16px;
171 | margin-right: 18px;
172 | display: inline-block;
173 | width: 24px;
174 | height: 24px;
175 | background: white;
176 | border: var(--border-width) solid var(--text-clr-light);
177 | }
178 | }
179 | input[type="checkbox"]:checked {
180 | & + label {
181 | &:before {
182 | background: var(--main-clr);
183 | }
184 | &:after {
185 | content: "";
186 | width: 20px;
187 | height: var(--border-width);
188 | background: var(--secondary-clr);
189 | display: inline-block;
190 | position: absolute;
191 | bottom: 11px;
192 | left: 2px;
193 | transform: rotate(-45deg);
194 | }
195 | }
196 | }
197 | }
198 |
199 | //////
200 | .switcher {
201 | z-index: 999;
202 | position: absolute;
203 | top: 16px;
204 | right: 16px;
205 | user-select: none;
206 | input[type="checkbox"] {
207 | display: none;
208 | & + label {
209 | text-transform: uppercase;
210 | display: flex;
211 | align-items: center;
212 | position: relative;
213 | cursor: pointer;
214 | padding: 0;
215 | color: var(--secondary-clr);
216 | }
217 | & + label:after {
218 | content: "";
219 | position: absolute;
220 | top: 50%;
221 | transform: translateY(-50%);
222 | left: -42px;
223 | display: inline-block;
224 | width: 14px;
225 | height: 20px;
226 | background: white;
227 | border: var(--border-width) solid var(--secondary-clr);
228 | transition: all 0.08s ease-in-out;
229 | }
230 | & + label:before {
231 | content: "";
232 | position: absolute;
233 | left: -42px;
234 | margin-right: 10px;
235 | display: inline-block;
236 | width: 32px;
237 | height: 20px;
238 | background: var(--main-clr);
239 | border: var(--border-width) solid var(--secondary-clr);
240 | }
241 | }
242 | input[type="checkbox"]:checked {
243 | & + label {
244 | &:after {
245 | left: -24px;
246 | }
247 | }
248 | }
249 | }
250 |
251 | .primary-btn {
252 | @include btn-mixin(var(--secondary-clr), white, var(--secondary-clr));
253 | border: var(--border-width) solid var(--secondary-clr);
254 | color: #111;
255 | &.active {
256 | background: var(--main-clr);
257 | border: var(--border-width) solid var(--main-clr);
258 | color: var(--text-clr-dark);
259 | &:hover {
260 | background: var(--main-clr) !important;
261 | border: var(--border-width) solid var(--text-clr-dark) !important;
262 | color: var(--secondary-clr) !important;
263 | }
264 | }
265 | }
266 |
267 | .secondary-btn {
268 | @include btn-mixin(var(--secondary-clr), white, var(--secondary-clr));
269 | border: var(--border-width) solid var(--main-clr);
270 | color: #111;
271 | &.active {
272 | background: var(--main-clr);
273 | border: var(--border-width) solid var(--main-clr);
274 | color: var(--text-clr-dark);
275 | > svg {
276 | fill: var(--text-clr-dark);
277 | }
278 | &:hover {
279 | background: var(--main-clr) !important;
280 | border: var(--border-width) solid var(--text-clr-dark) !important;
281 | color: var(--secondary-clr) !important;
282 | > svg {
283 | fill: var(--text-clr-dark) !important;
284 | }
285 | }
286 | }
287 | }
288 |
--------------------------------------------------------------------------------
/src/scss/_dragndrop-zone.scss:
--------------------------------------------------------------------------------
1 | .dropzone-preview {
2 | z-index: 99999;
3 | position: absolute;
4 | pointer-events: none;
5 | width: 100%;
6 | height: 100%;
7 | opacity: 0;
8 | transition: all 0.2s ease;
9 | .info {
10 | z-index: 99999;
11 | padding: 100px;
12 | border: var(--text-clr-dark) solid var(--border-width);
13 | transform: translate(-50%, -50%);
14 | background-color: var(--text-clr-light);
15 | position: absolute;
16 | top: 50%;
17 | left: 50%;
18 | display: flex;
19 | flex-direction: column;
20 | align-items: center;
21 | }
22 | .overlay {
23 | background: var(--main-clr);
24 | width: 100%;
25 | height: 100%;
26 | opacity: 0.8;
27 | }
28 | .caption {
29 | color: var(--text-clr-dark);
30 | line-height: 1.3;
31 | }
32 | .icon {
33 | display: inline-block;
34 | width: 230px;
35 | height: 176px;
36 | background-image: url(../images/dragndrop-icon.svg);
37 | background-size: cover;
38 | background-repeat: no-repeat;
39 | }
40 | &.show {
41 | opacity: 1;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/scss/_if-mobile.scss:
--------------------------------------------------------------------------------
1 | #if-mobile {
2 | position: absolute;
3 | top: 50%;
4 | left: 50%;
5 | transform: translate(-50%, -50%);
6 | z-index: 99999;
7 | width: 90%;
8 | height: 90%;
9 | background: rgba($color: #fff, $alpha: 1);
10 | border: 8px solid var(--main-clr);
11 | .icon {
12 | width: 100%;
13 | height: 100%;
14 | background-size: 60%;
15 | background-position: center;
16 | background-image: url(../images/text-for-mobile.svg);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/scss/_loader.scss:
--------------------------------------------------------------------------------
1 | #loader {
2 | z-index: 999999;
3 | position: absolute;
4 | width: 100vw;
5 | height: 100vh;
6 | background: white;
7 | display: flex;
8 | }
9 |
10 | #loader-spinner {
11 | display: inline-block;
12 | width: 60px;
13 | height: 60px;
14 | background-color: var(--secondary-clr);
15 | margin: auto;
16 | animation-name: transform-animation;
17 | animation-duration: 0.5s;
18 | animation-iteration-count: infinite;
19 | animation-direction: alternate;
20 | }
21 |
22 | @keyframes transform-animation {
23 | from {
24 | background-color: var(--secondary-clr);
25 | transform: scale(0.1) rotate(0deg);
26 | }
27 | to {
28 | background-color: var(--main-clr);
29 | transform: scale(0.9) rotate(45deg);
30 | border-radius: 100%;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/scss/_mixins.scss:
--------------------------------------------------------------------------------
1 | @mixin content-block {
2 | cursor: default;
3 | background: var(--secondary-clr);
4 | padding: 24px;
5 | margin-bottom: 8px;
6 | color: var(--text-clr-light);
7 | font-size: 14px;
8 | overflow: hidden;
9 | position: relative;
10 | // max-width: 320px;
11 | ul {
12 | padding-left: 24px;
13 | margin: 0;
14 | list-style: none;
15 | li {
16 | &:before {
17 | position: absolute;
18 | content: "\2022";
19 | color: var(--main-clr);
20 | font-weight: bold;
21 | display: inline-block;
22 | width: 1em;
23 | margin-left: -24px;
24 | }
25 | }
26 | }
27 | p {
28 | margin-top: 8px;
29 | margin-bottom: 16px;
30 | line-height: 1.4;
31 | }
32 | h3 {
33 | font-size: 14px;
34 | opacity: 0.4;
35 | margin-top: 0;
36 | margin-bottom: 20px;
37 | text-transform: uppercase;
38 | }
39 | &.hide {
40 | opacity: 0;
41 | height: 0;
42 | padding: 0 24px;
43 | margin-bottom: 0;
44 | }
45 | }
46 |
47 | @mixin btn-mixin($font-clr, $bck-clr, $hover-bck-clr) {
48 | cursor: pointer;
49 | user-select: none;
50 | font-family: "IBM Plex Mono", monospace;
51 | font-size: 15px;
52 | text-transform: uppercase;
53 | padding: 0 16px;
54 | height: 54px;
55 | background: $bck-clr;
56 | color: $font-clr;
57 | transition: all 0.05s ease-in-out;
58 | &:focus {
59 | outline: none;
60 | }
61 | &:hover {
62 | background: $hover-bck-clr !important;
63 | color: var(--main-clr) !important;
64 | svg {
65 | fill: var(--main-clr) !important;
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/scss/_svg.scss:
--------------------------------------------------------------------------------
1 | #svg-element {
2 | overflow: visible;
3 | width: 100%;
4 | height: 100%;
5 | }
6 |
7 | #svg-container {
8 | position: relative;
9 | background-color: var(--background-clr);
10 | }
11 |
12 | #svg-control {
13 | width: 100%;
14 | height: 100%;
15 | overflow: visible;
16 | position: absolute;
17 | &:hover {
18 | #control-path {
19 | stroke: var(--active-drag-clr);
20 | opacity: 0.6;
21 | }
22 | .control-point {
23 | opacity: 1;
24 | fill: var(--active-drag-clr);
25 | }
26 | }
27 | &.show {
28 | outline: 1px solid var(--checker-rect);
29 | }
30 | }
31 |
32 | #scale-wrap {
33 | margin: auto;
34 | }
35 |
36 | #control-path {
37 | fill: none;
38 | stroke: var(--text-clr-dark);
39 | // stroke-width: 1px;
40 | opacity: 0.15;
41 | }
42 |
43 | .control-point {
44 | opacity: 1;
45 | fill: var(--secondary-clr);
46 | // stroke-width: 2px;
47 | }
48 |
--------------------------------------------------------------------------------
/src/scss/_variables.scss:
--------------------------------------------------------------------------------
1 | :root {
2 | --border-width: 2px;
3 | --main-clr: #35ffce;
4 | --secondary-clr: #111;
5 | --background-clr: white;
6 | --text-clr-light: white;
7 | --text-clr-dark: #111;
8 | --btn-border-clr: var(--main-clr);
9 | --active-drag-clr: #f45;
10 | --checker-rect: rgba(0, 0, 0, 0.1);
11 | }
12 |
13 | .dark-theme {
14 | --main-clr: #111;
15 | --secondary-clr: #35ffce;
16 | --background-clr: rgb(34, 34, 34);
17 | --text-clr-light: #111;
18 | --text-clr-dark: white;
19 | --btn-border-clr: white;
20 | --checker-rect: rgba(255, 255, 255, 0.1);
21 | }
22 |
--------------------------------------------------------------------------------
/src/scss/normalize.css:
--------------------------------------------------------------------------------
1 | /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
2 |
3 | /* Document
4 | ========================================================================== */
5 |
6 | /**
7 | * 1. Correct the line height in all browsers.
8 | * 2. Prevent adjustments of font size after orientation changes in iOS.
9 | */
10 |
11 | html {
12 | line-height: 1.15; /* 1 */
13 | -webkit-text-size-adjust: 100%; /* 2 */
14 | }
15 |
16 | /* Sections
17 | ========================================================================== */
18 |
19 | /**
20 | * Remove the margin in all browsers.
21 | */
22 |
23 | body {
24 | margin: 0;
25 | }
26 |
27 | /**
28 | * Render the `main` element consistently in IE.
29 | */
30 |
31 | main {
32 | display: block;
33 | }
34 |
35 | /**
36 | * Correct the font size and margin on `h1` elements within `section` and
37 | * `article` contexts in Chrome, Firefox, and Safari.
38 | */
39 |
40 | h1 {
41 | font-size: 2em;
42 | margin: 0.67em 0;
43 | }
44 |
45 | /* Grouping content
46 | ========================================================================== */
47 |
48 | /**
49 | * 1. Add the correct box sizing in Firefox.
50 | * 2. Show the overflow in Edge and IE.
51 | */
52 |
53 | hr {
54 | box-sizing: content-box; /* 1 */
55 | height: 0; /* 1 */
56 | overflow: visible; /* 2 */
57 | }
58 |
59 | /**
60 | * 1. Correct the inheritance and scaling of font size in all browsers.
61 | * 2. Correct the odd `em` font sizing in all browsers.
62 | */
63 |
64 | pre {
65 | font-family: monospace, monospace; /* 1 */
66 | font-size: 1em; /* 2 */
67 | }
68 |
69 | /* Text-level semantics
70 | ========================================================================== */
71 |
72 | /**
73 | * Remove the gray background on active links in IE 10.
74 | */
75 |
76 | a {
77 | background-color: transparent;
78 | }
79 |
80 | /**
81 | * 1. Remove the bottom border in Chrome 57-
82 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
83 | */
84 |
85 | abbr[title] {
86 | border-bottom: none; /* 1 */
87 | text-decoration: underline; /* 2 */
88 | text-decoration: underline dotted; /* 2 */
89 | }
90 |
91 | /**
92 | * Add the correct font weight in Chrome, Edge, and Safari.
93 | */
94 |
95 | b,
96 | strong {
97 | font-weight: bolder;
98 | }
99 |
100 | /**
101 | * 1. Correct the inheritance and scaling of font size in all browsers.
102 | * 2. Correct the odd `em` font sizing in all browsers.
103 | */
104 |
105 | code,
106 | kbd,
107 | samp {
108 | font-family: monospace, monospace; /* 1 */
109 | font-size: 1em; /* 2 */
110 | }
111 |
112 | /**
113 | * Add the correct font size in all browsers.
114 | */
115 |
116 | small {
117 | font-size: 80%;
118 | }
119 |
120 | /**
121 | * Prevent `sub` and `sup` elements from affecting the line height in
122 | * all browsers.
123 | */
124 |
125 | sub,
126 | sup {
127 | font-size: 75%;
128 | line-height: 0;
129 | position: relative;
130 | vertical-align: baseline;
131 | }
132 |
133 | sub {
134 | bottom: -0.25em;
135 | }
136 |
137 | sup {
138 | top: -0.5em;
139 | }
140 |
141 | /* Embedded content
142 | ========================================================================== */
143 |
144 | /**
145 | * Remove the border on images inside links in IE 10.
146 | */
147 |
148 | img {
149 | border-style: none;
150 | }
151 |
152 | /* Forms
153 | ========================================================================== */
154 |
155 | /**
156 | * 1. Change the font styles in all browsers.
157 | * 2. Remove the margin in Firefox and Safari.
158 | */
159 |
160 | button,
161 | input,
162 | optgroup,
163 | select,
164 | textarea {
165 | font-family: inherit; /* 1 */
166 | font-size: 100%; /* 1 */
167 | line-height: 1.15; /* 1 */
168 | margin: 0; /* 2 */
169 | }
170 |
171 | /**
172 | * Show the overflow in IE.
173 | * 1. Show the overflow in Edge.
174 | */
175 |
176 | button,
177 | input { /* 1 */
178 | overflow: visible;
179 | }
180 |
181 | /**
182 | * Remove the inheritance of text transform in Edge, Firefox, and IE.
183 | * 1. Remove the inheritance of text transform in Firefox.
184 | */
185 |
186 | button,
187 | select { /* 1 */
188 | text-transform: none;
189 | }
190 |
191 | /**
192 | * Correct the inability to style clickable types in iOS and Safari.
193 | */
194 |
195 | button,
196 | [type="button"],
197 | [type="reset"],
198 | [type="submit"] {
199 | -webkit-appearance: button;
200 | }
201 |
202 | /**
203 | * Remove the inner border and padding in Firefox.
204 | */
205 |
206 | button::-moz-focus-inner,
207 | [type="button"]::-moz-focus-inner,
208 | [type="reset"]::-moz-focus-inner,
209 | [type="submit"]::-moz-focus-inner {
210 | border-style: none;
211 | padding: 0;
212 | }
213 |
214 | /**
215 | * Restore the focus styles unset by the previous rule.
216 | */
217 |
218 | button:-moz-focusring,
219 | [type="button"]:-moz-focusring,
220 | [type="reset"]:-moz-focusring,
221 | [type="submit"]:-moz-focusring {
222 | outline: 1px dotted ButtonText;
223 | }
224 |
225 | /**
226 | * Correct the padding in Firefox.
227 | */
228 |
229 | fieldset {
230 | padding: 0.35em 0.75em 0.625em;
231 | }
232 |
233 | /**
234 | * 1. Correct the text wrapping in Edge and IE.
235 | * 2. Correct the color inheritance from `fieldset` elements in IE.
236 | * 3. Remove the padding so developers are not caught out when they zero out
237 | * `fieldset` elements in all browsers.
238 | */
239 |
240 | legend {
241 | box-sizing: border-box; /* 1 */
242 | color: inherit; /* 2 */
243 | display: table; /* 1 */
244 | max-width: 100%; /* 1 */
245 | padding: 0; /* 3 */
246 | white-space: normal; /* 1 */
247 | }
248 |
249 | /**
250 | * Add the correct vertical alignment in Chrome, Firefox, and Opera.
251 | */
252 |
253 | progress {
254 | vertical-align: baseline;
255 | }
256 |
257 | /**
258 | * Remove the default vertical scrollbar in IE 10+.
259 | */
260 |
261 | textarea {
262 | overflow: auto;
263 | }
264 |
265 | /**
266 | * 1. Add the correct box sizing in IE 10.
267 | * 2. Remove the padding in IE 10.
268 | */
269 |
270 | [type="checkbox"],
271 | [type="radio"] {
272 | box-sizing: border-box; /* 1 */
273 | padding: 0; /* 2 */
274 | }
275 |
276 | /**
277 | * Correct the cursor style of increment and decrement buttons in Chrome.
278 | */
279 |
280 | [type="number"]::-webkit-inner-spin-button,
281 | [type="number"]::-webkit-outer-spin-button {
282 | height: auto;
283 | }
284 |
285 | /**
286 | * 1. Correct the odd appearance in Chrome and Safari.
287 | * 2. Correct the outline style in Safari.
288 | */
289 |
290 | [type="search"] {
291 | -webkit-appearance: textfield; /* 1 */
292 | outline-offset: -2px; /* 2 */
293 | }
294 |
295 | /**
296 | * Remove the inner padding in Chrome and Safari on macOS.
297 | */
298 |
299 | [type="search"]::-webkit-search-decoration {
300 | -webkit-appearance: none;
301 | }
302 |
303 | /**
304 | * 1. Correct the inability to style clickable types in iOS and Safari.
305 | * 2. Change font properties to `inherit` in Safari.
306 | */
307 |
308 | ::-webkit-file-upload-button {
309 | -webkit-appearance: button; /* 1 */
310 | font: inherit; /* 2 */
311 | }
312 |
313 | /* Interactive
314 | ========================================================================== */
315 |
316 | /*
317 | * Add the correct display in Edge, IE 10+, and Firefox.
318 | */
319 |
320 | details {
321 | display: block;
322 | }
323 |
324 | /*
325 | * Add the correct display in all browsers.
326 | */
327 |
328 | summary {
329 | display: list-item;
330 | }
331 |
332 | /* Misc
333 | ========================================================================== */
334 |
335 | /**
336 | * Add the correct display in IE 10+.
337 | */
338 |
339 | template {
340 | display: none;
341 | }
342 |
343 | /**
344 | * Add the correct display in IE 10.
345 | */
346 |
347 | [hidden] {
348 | display: none;
349 | }
350 |
--------------------------------------------------------------------------------
/src/scss/styles.scss:
--------------------------------------------------------------------------------
1 | @import "./normalize";
2 | @import "./_variables";
3 | @import "./mixins";
4 | @import "./_base";
5 | @import "./svg";
6 | @import "./dragndrop-zone";
7 | @import "./controls";
8 | @import "./loader";
9 | @import "./if-mobile";
10 | @import url("https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@500&display=swap");
11 |
12 | #app {
13 | display: flex;
14 | flex-direction: column;
15 | height: 100vh;
16 | width: 100%;
17 | font-family: "IBM Plex Mono", monospace;
18 | background-color: var(--background-clr);
19 | }
20 |
21 | //
22 | .links {
23 | display: flex;
24 | flex-wrap: wrap;
25 | padding: 16px 0 8px;
26 | }
27 |
28 | .social-media {
29 | display: inline-block;
30 | margin-right: 20px;
31 | display: flex;
32 | align-items: center;
33 | text-decoration: none;
34 | span {
35 | color: var(--main-clr);
36 | margin: 0 8px;
37 | font-size: 14px;
38 | transition: all 0.1s ease;
39 | }
40 | svg {
41 | fill: var(--main-clr);
42 | transform: scale(0.8);
43 | transition: all 0.1s ease;
44 | }
45 | &:hover {
46 | span {
47 | color: white;
48 | }
49 | svg {
50 | fill: white;
51 | }
52 | }
53 | }
54 |
55 | .checkerboard-pattern {
56 | background-image: linear-gradient(
57 | 45deg,
58 | var(--checker-rect) 25%,
59 | transparent 25%
60 | ),
61 | linear-gradient(-45deg, var(--checker-rect) 25%, transparent 25%),
62 | linear-gradient(45deg, transparent 75%, var(--checker-rect) 75%),
63 | linear-gradient(-45deg, transparent 75%, var(--checker-rect) 75%) !important;
64 | background-size: 20px 20px;
65 | background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
66 | }
67 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Assets Config file
3 | */
4 |
5 | const serverConfiguration = {
6 | internal: {
7 | server: {
8 | baseDir: './',
9 | },
10 | port: 3000,
11 | },
12 | external: {
13 | proxy: 'http://localhost:9000/path/to/project/',
14 | },
15 | };
16 |
17 | const path = require('path');
18 | const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
19 | const BrowserSyncPlugin = require('browser-sync-webpack-plugin');
20 | const MiniCssExtractPlugin = require('mini-css-extract-plugin');
21 | const HtmlWebpackPlugin = require('html-webpack-plugin');
22 | const { CleanWebpackPlugin } = require('clean-webpack-plugin');
23 | const TerserPlugin = require('terser-webpack-plugin');
24 | const CopyWebpackPlugin = require('copy-webpack-plugin');
25 | const ImageMinPlugin = require('imagemin-webpack-plugin').default;
26 |
27 | let targetServerConfiguration = serverConfiguration.internal;
28 |
29 | const config = function (env, args) {
30 | if (args.externalServer !== undefined && args.externalServer) {
31 | targetServerConfiguration = serverConfiguration.external;
32 | }
33 |
34 | return {
35 | entry: {
36 | app: './src/js/index.js',
37 | },
38 | output: {
39 | filename: 'js/[name].js',
40 | path: path.resolve(__dirname, 'dist'),
41 | },
42 | module: {
43 | rules: [
44 | {
45 | test: /\.scss$/,
46 | use: [
47 | 'style-loader',
48 | MiniCssExtractPlugin.loader,
49 | 'css-loader',
50 | 'postcss-loader',
51 | 'sass-loader',
52 | ],
53 | },
54 | {
55 | test: /\.js$/,
56 | exclude: /(node_modules|bower_components)/,
57 | loader: 'babel-loader',
58 | },
59 | {
60 | test: /\.(png|gif|jpg|jpeg)$/,
61 | use: [
62 | {
63 | loader: 'url-loader',
64 | options: {
65 | name: 'images/design/[name].[hash:6].[ext]',
66 | publicPath: '../',
67 | limit: 8192,
68 | },
69 | },
70 | ],
71 | },
72 | {
73 | test: /\.(eot|svg|ttf|woff|woff2)$/,
74 | use: [
75 | {
76 | loader: 'url-loader',
77 | options: {
78 | name: 'fonts/[name].[hash:6].[ext]',
79 | publicPath: '../',
80 | limit: 8192,
81 | },
82 | },
83 | ],
84 | },
85 | ],
86 | },
87 | optimization: {
88 | minimizer: [
89 | new TerserPlugin({
90 | parallel: true,
91 | }),
92 | new OptimizeCssAssetsPlugin({}),
93 | ],
94 | },
95 | watchOptions: {
96 | poll: 1000,
97 | ignored: /node_modules/,
98 | },
99 | plugins: [
100 | new BrowserSyncPlugin({
101 | ...targetServerConfiguration,
102 | files: ['src/*'],
103 | ghostMode: {
104 | clicks: false,
105 | location: false,
106 | forms: false,
107 | scroll: false,
108 | },
109 | injectChanges: true,
110 | logFileChanges: true,
111 | logLevel: 'debug',
112 | logPrefix: 'wepback',
113 | notify: true,
114 | reloadDelay: 0,
115 | }),
116 | new HtmlWebpackPlugin({
117 | inject: true,
118 | hash: false,
119 | filename: '../index.html',
120 | template: path.resolve(__dirname, 'src', 'index.html'),
121 | }),
122 | new MiniCssExtractPlugin({
123 | filename: 'css/[name].css',
124 | }),
125 | new ImageMinPlugin({ test: /\.(jpg|jpeg|png|gif|svg)$/i }),
126 | new CleanWebpackPlugin({
127 | /**
128 | * Some plugins used do not correctly save to webpack's asset list.
129 | * Disable automatic asset cleaning until resolved
130 | */
131 | cleanStaleWebpackAssets: false,
132 | verbose: true,
133 | }),
134 | new CopyWebpackPlugin([
135 | {
136 | from: path.resolve(__dirname, 'src', 'images'),
137 | to: path.resolve(__dirname, 'dist', 'images'),
138 | toType: 'dir',
139 | },
140 | ]),
141 | ],
142 | };
143 | };
144 |
145 | module.exports = config;
146 |
--------------------------------------------------------------------------------