kopia lustrzana https://github.com/msurguy/SquiggleCam
Fix conflicts
rodzic
4510054107
commit
1ae2248226
|
@ -1,467 +0,0 @@
|
||||||
body {
|
|
||||||
width: 100%;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 1.42857143;
|
|
||||||
color: #333;
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg {
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
video {
|
|
||||||
width: 100px;
|
|
||||||
height: 100px;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings-container {
|
|
||||||
-webkit-box-flex: 1;
|
|
||||||
-ms-flex: 1;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-container {
|
|
||||||
-webkit-box-flex: 0;
|
|
||||||
-ms-flex: 0 0 100px;
|
|
||||||
flex: 0 0 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#status {
|
|
||||||
display: inline-block;
|
|
||||||
width: 100px;
|
|
||||||
text-align: center;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions {
|
|
||||||
background-color: black;
|
|
||||||
color: white;
|
|
||||||
z-index: 100;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 210px;
|
|
||||||
display: -webkit-box;
|
|
||||||
display: -ms-flexbox;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sliders {
|
|
||||||
display: -webkit-box;
|
|
||||||
display: -ms-flexbox;
|
|
||||||
display: flex;
|
|
||||||
-webkit-box-orient: horizontal;
|
|
||||||
-webkit-box-direction: normal;
|
|
||||||
-ms-flex-direction: row;
|
|
||||||
flex-direction: row;
|
|
||||||
-webkit-box-flex: 1;
|
|
||||||
-ms-flex: 1;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col-50 {
|
|
||||||
position: relative;
|
|
||||||
margin: 0;
|
|
||||||
padding: 10px;
|
|
||||||
-webkit-box-flex: 1;
|
|
||||||
-ms-flex: 1;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#drawButton {
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rangeslider {
|
|
||||||
position: relative;
|
|
||||||
display: block;
|
|
||||||
cursor: pointer;
|
|
||||||
height: 25px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rangeslider__fill,
|
|
||||||
.rangeslider__fill__bg {
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
height: 2px;
|
|
||||||
z-index: 2;
|
|
||||||
background: #29e;
|
|
||||||
border-radius: 10px;
|
|
||||||
will-change: width;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rangeslider__handle {
|
|
||||||
will-change: width, height, top;
|
|
||||||
width: 18px;
|
|
||||||
height: 18px;
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
background: #29e;
|
|
||||||
display: inline-block;
|
|
||||||
z-index: 3;
|
|
||||||
cursor: pointer;
|
|
||||||
border: solid 2px #ffffff;
|
|
||||||
border-radius: 50%;
|
|
||||||
-webkit-transition: width 0.1s ease-in-out, height 0.1s ease-in-out, top 0.1s ease-in-out;
|
|
||||||
transition: width 0.1s ease-in-out, height 0.1s ease-in-out, top 0.1s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rangeslider__handle:active {
|
|
||||||
background: #107ecd;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rangeslider__fill__bg {
|
|
||||||
background: #ccc;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rangeslider--disabled {
|
|
||||||
opacity: 0.4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 8px 8px;
|
|
||||||
margin-bottom: 0;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: normal;
|
|
||||||
line-height: 1.42857143;
|
|
||||||
text-align: center;
|
|
||||||
white-space: nowrap;
|
|
||||||
vertical-align: middle;
|
|
||||||
-ms-touch-action: manipulation;
|
|
||||||
touch-action: manipulation;
|
|
||||||
cursor: pointer;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-moz-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
background-image: none;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn:focus,
|
|
||||||
.btn:active:focus,
|
|
||||||
.btn.active:focus,
|
|
||||||
.btn.focus,
|
|
||||||
.btn:active.focus,
|
|
||||||
.btn.active.focus {
|
|
||||||
outline: thin dotted;
|
|
||||||
outline: 5px auto -webkit-focus-ring-color;
|
|
||||||
outline-offset: -2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn:hover,
|
|
||||||
.btn:focus,
|
|
||||||
.btn.focus {
|
|
||||||
color: #333;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn:active,
|
|
||||||
.btn.active {
|
|
||||||
background-image: none;
|
|
||||||
outline: 0;
|
|
||||||
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
|
|
||||||
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn.disabled,
|
|
||||||
.btn[disabled],
|
|
||||||
fieldset[disabled] .btn {
|
|
||||||
cursor: not-allowed;
|
|
||||||
filter: alpha(opacity=65);
|
|
||||||
-webkit-box-shadow: none;
|
|
||||||
box-shadow: none;
|
|
||||||
opacity: .65;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.btn.disabled,
|
|
||||||
fieldset[disabled] a.btn {
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #337ab7;
|
|
||||||
border-color: #2e6da4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary:focus,
|
|
||||||
.btn-primary.focus {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #286090;
|
|
||||||
border-color: #122b40;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary:hover {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #286090;
|
|
||||||
border-color: #204d74;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary:active,
|
|
||||||
.btn-primary.active,
|
|
||||||
.open > .dropdown-toggle.btn-primary {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #286090;
|
|
||||||
border-color: #204d74;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary:active:hover,
|
|
||||||
.btn-primary.active:hover,
|
|
||||||
.open > .dropdown-toggle.btn-primary:hover,
|
|
||||||
.btn-primary:active:focus,
|
|
||||||
.btn-primary.active:focus,
|
|
||||||
.open > .dropdown-toggle.btn-primary:focus,
|
|
||||||
.btn-primary:active.focus,
|
|
||||||
.btn-primary.active.focus,
|
|
||||||
.open > .dropdown-toggle.btn-primary.focus {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #204d74;
|
|
||||||
border-color: #122b40;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary:active,
|
|
||||||
.btn-primary.active,
|
|
||||||
.open > .dropdown-toggle.btn-primary {
|
|
||||||
background-image: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary.disabled:hover,
|
|
||||||
.btn-primary[disabled]:hover,
|
|
||||||
fieldset[disabled] .btn-primary:hover,
|
|
||||||
.btn-primary.disabled:focus,
|
|
||||||
.btn-primary[disabled]:focus,
|
|
||||||
fieldset[disabled] .btn-primary:focus,
|
|
||||||
.btn-primary.disabled.focus,
|
|
||||||
.btn-primary[disabled].focus,
|
|
||||||
fieldset[disabled] .btn-primary.focus {
|
|
||||||
background-color: #337ab7;
|
|
||||||
border-color: #2e6da4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-success {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #5cb85c;
|
|
||||||
border-color: #4cae4c;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-success:focus,
|
|
||||||
.btn-success.focus {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #449d44;
|
|
||||||
border-color: #255625;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-success:hover {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #449d44;
|
|
||||||
border-color: #398439;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-success:active,
|
|
||||||
.btn-success.active,
|
|
||||||
.open > .dropdown-toggle.btn-success {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #449d44;
|
|
||||||
border-color: #398439;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-success:active:hover,
|
|
||||||
.btn-success.active:hover,
|
|
||||||
.open > .dropdown-toggle.btn-success:hover,
|
|
||||||
.btn-success:active:focus,
|
|
||||||
.btn-success.active:focus,
|
|
||||||
.open > .dropdown-toggle.btn-success:focus,
|
|
||||||
.btn-success:active.focus,
|
|
||||||
.btn-success.active.focus,
|
|
||||||
.open > .dropdown-toggle.btn-success.focus {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #398439;
|
|
||||||
border-color: #255625;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-success:active,
|
|
||||||
.btn-success.active,
|
|
||||||
.open > .dropdown-toggle.btn-success {
|
|
||||||
background-image: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-success.disabled:hover,
|
|
||||||
.btn-success[disabled]:hover,
|
|
||||||
fieldset[disabled] .btn-success:hover,
|
|
||||||
.btn-success.disabled:focus,
|
|
||||||
.btn-success[disabled]:focus,
|
|
||||||
fieldset[disabled] .btn-success:focus,
|
|
||||||
.btn-success.disabled.focus,
|
|
||||||
.btn-success[disabled].focus,
|
|
||||||
fieldset[disabled] .btn-success.focus {
|
|
||||||
background-color: #5cb85c;
|
|
||||||
border-color: #4cae4c;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-info {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #5bc0de;
|
|
||||||
border-color: #46b8da;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-info:focus,
|
|
||||||
.btn-info.focus {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #31b0d5;
|
|
||||||
border-color: #1b6d85;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-info:hover {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #31b0d5;
|
|
||||||
border-color: #269abc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-info:active,
|
|
||||||
.btn-info.active,
|
|
||||||
.open > .dropdown-toggle.btn-info {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #31b0d5;
|
|
||||||
border-color: #269abc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-info:active:hover,
|
|
||||||
.btn-info.active:hover,
|
|
||||||
.open > .dropdown-toggle.btn-info:hover,
|
|
||||||
.btn-info:active:focus,
|
|
||||||
.btn-info.active:focus,
|
|
||||||
.open > .dropdown-toggle.btn-info:focus,
|
|
||||||
.btn-info:active.focus,
|
|
||||||
.btn-info.active.focus,
|
|
||||||
.open > .dropdown-toggle.btn-info.focus {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #269abc;
|
|
||||||
border-color: #1b6d85;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-info:active,
|
|
||||||
.btn-info.active,
|
|
||||||
.open > .dropdown-toggle.btn-info {
|
|
||||||
background-image: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-info.disabled:hover,
|
|
||||||
.btn-info[disabled]:hover,
|
|
||||||
fieldset[disabled] .btn-info:hover,
|
|
||||||
.btn-info.disabled:focus,
|
|
||||||
.btn-info[disabled]:focus,
|
|
||||||
fieldset[disabled] .btn-info:focus,
|
|
||||||
.btn-info.disabled.focus,
|
|
||||||
.btn-info[disabled].focus,
|
|
||||||
fieldset[disabled] .btn-info.focus {
|
|
||||||
background-color: #5bc0de;
|
|
||||||
border-color: #46b8da;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-danger {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #d9534f;
|
|
||||||
border-color: #d43f3a;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-danger:focus,
|
|
||||||
.btn-danger.focus {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #c9302c;
|
|
||||||
border-color: #761c19;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-danger:hover {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #c9302c;
|
|
||||||
border-color: #ac2925;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-danger:active,
|
|
||||||
.btn-danger.active,
|
|
||||||
.open > .dropdown-toggle.btn-danger {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #c9302c;
|
|
||||||
border-color: #ac2925;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-danger:active:hover,
|
|
||||||
.btn-danger.active:hover,
|
|
||||||
.open > .dropdown-toggle.btn-danger:hover,
|
|
||||||
.btn-danger:active:focus,
|
|
||||||
.btn-danger.active:focus,
|
|
||||||
.open > .dropdown-toggle.btn-danger:focus,
|
|
||||||
.btn-danger:active.focus,
|
|
||||||
.btn-danger.active.focus,
|
|
||||||
.open > .dropdown-toggle.btn-danger.focus {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #ac2925;
|
|
||||||
border-color: #761c19;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-danger:active,
|
|
||||||
.btn-danger.active,
|
|
||||||
.open > .dropdown-toggle.btn-danger {
|
|
||||||
background-image: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-danger.disabled:hover,
|
|
||||||
.btn-danger[disabled]:hover,
|
|
||||||
fieldset[disabled] .btn-danger:hover,
|
|
||||||
.btn-danger.disabled:focus,
|
|
||||||
.btn-danger[disabled]:focus,
|
|
||||||
fieldset[disabled] .btn-danger:focus,
|
|
||||||
.btn-danger.disabled.focus,
|
|
||||||
.btn-danger[disabled].focus,
|
|
||||||
fieldset[disabled] .btn-danger.focus {
|
|
||||||
background-color: #d9534f;
|
|
||||||
border-color: #d43f3a;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:active,
|
|
||||||
a:hover {
|
|
||||||
outline: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #337ab7;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover,
|
|
||||||
a:focus {
|
|
||||||
color: #23527c;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:focus {
|
|
||||||
outline: thin dotted;
|
|
||||||
outline: 5px auto -webkit-focus-ring-color;
|
|
||||||
outline-offset: -2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-block {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,151 +0,0 @@
|
||||||
/******/ (function(modules) { // webpackBootstrap
|
|
||||||
/******/ // The module cache
|
|
||||||
/******/ var installedModules = {};
|
|
||||||
/******/
|
|
||||||
/******/ // The require function
|
|
||||||
/******/ function __webpack_require__(moduleId) {
|
|
||||||
/******/
|
|
||||||
/******/ // Check if module is in cache
|
|
||||||
/******/ if(installedModules[moduleId]) {
|
|
||||||
/******/ return installedModules[moduleId].exports;
|
|
||||||
/******/ }
|
|
||||||
/******/ // Create a new module (and put it into the cache)
|
|
||||||
/******/ var module = installedModules[moduleId] = {
|
|
||||||
/******/ i: moduleId,
|
|
||||||
/******/ l: false,
|
|
||||||
/******/ exports: {}
|
|
||||||
/******/ };
|
|
||||||
/******/
|
|
||||||
/******/ // Execute the module function
|
|
||||||
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
|
||||||
/******/
|
|
||||||
/******/ // Flag the module as loaded
|
|
||||||
/******/ module.l = true;
|
|
||||||
/******/
|
|
||||||
/******/ // Return the exports of the module
|
|
||||||
/******/ return module.exports;
|
|
||||||
/******/ }
|
|
||||||
/******/
|
|
||||||
/******/
|
|
||||||
/******/ // expose the modules object (__webpack_modules__)
|
|
||||||
/******/ __webpack_require__.m = modules;
|
|
||||||
/******/
|
|
||||||
/******/ // expose the module cache
|
|
||||||
/******/ __webpack_require__.c = installedModules;
|
|
||||||
/******/
|
|
||||||
/******/ // define getter function for harmony exports
|
|
||||||
/******/ __webpack_require__.d = function(exports, name, getter) {
|
|
||||||
/******/ if(!__webpack_require__.o(exports, name)) {
|
|
||||||
/******/ Object.defineProperty(exports, name, {
|
|
||||||
/******/ configurable: false,
|
|
||||||
/******/ enumerable: true,
|
|
||||||
/******/ get: getter
|
|
||||||
/******/ });
|
|
||||||
/******/ }
|
|
||||||
/******/ };
|
|
||||||
/******/
|
|
||||||
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
|
||||||
/******/ __webpack_require__.n = function(module) {
|
|
||||||
/******/ var getter = module && module.__esModule ?
|
|
||||||
/******/ function getDefault() { return module['default']; } :
|
|
||||||
/******/ function getModuleExports() { return module; };
|
|
||||||
/******/ __webpack_require__.d(getter, 'a', getter);
|
|
||||||
/******/ return getter;
|
|
||||||
/******/ };
|
|
||||||
/******/
|
|
||||||
/******/ // Object.prototype.hasOwnProperty.call
|
|
||||||
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
|
|
||||||
/******/
|
|
||||||
/******/ // __webpack_public_path__
|
|
||||||
/******/ __webpack_require__.p = "";
|
|
||||||
/******/
|
|
||||||
/******/ // Load entry module and return exports
|
|
||||||
/******/ return __webpack_require__(__webpack_require__.s = 467);
|
|
||||||
/******/ })
|
|
||||||
/************************************************************************/
|
|
||||||
/******/ ({
|
|
||||||
|
|
||||||
/***/ 467:
|
|
||||||
/***/ (function(module, exports, __webpack_require__) {
|
|
||||||
|
|
||||||
module.exports = __webpack_require__(468);
|
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
|
||||||
|
|
||||||
/***/ 468:
|
|
||||||
/***/ (function(module, exports) {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* imageAnalyzer Web Worker accepts image data and configuration parameters
|
|
||||||
* and returns array of x,y coordinates to draw squiggles
|
|
||||||
*
|
|
||||||
* It analyzes pixels for their average brightness and if brightness is closer to 0 -> makes lines less
|
|
||||||
* squiggly, if closer to 255 -> more squiggly
|
|
||||||
*
|
|
||||||
* This script runs in a webWorker thread in order to speed up processing of the image and
|
|
||||||
* to detach the processing from the UI updates
|
|
||||||
*
|
|
||||||
* Majority of the logic in this script is taken from https://github.com/gwygonik/SquiggleDraw for the following exceptions:
|
|
||||||
* - I implemented contrast adjustment that can be helpful in certain light conditions
|
|
||||||
* - I increased the processing speed by removing unnecessary assignments and moving variable
|
|
||||||
* declarations out of the processing loop
|
|
||||||
*/
|
|
||||||
|
|
||||||
self.addEventListener('message', function (e) {
|
|
||||||
// Gather all necessary data from the main thread
|
|
||||||
var config = e.data.config;
|
|
||||||
var imagePixels = e.data.data;
|
|
||||||
var width = e.data.config.WIDTH;
|
|
||||||
var height = e.data.config.HEIGHT;
|
|
||||||
|
|
||||||
// Create some defaults for squiggle-point array
|
|
||||||
var squiggleData = [];
|
|
||||||
var r = 5;
|
|
||||||
var a = 0;
|
|
||||||
var b = void 0;
|
|
||||||
var z = void 0;
|
|
||||||
var currentCoordinates = []; // create empty array for storing x,y coordinate pairs
|
|
||||||
var currentVerticalPixelIndex = 0;
|
|
||||||
var currentHorizontalPixelIndex = 0;
|
|
||||||
var contrastFactor = 259 * (config.CONTRAST_ADJUSTMENT + 255) / (255 * (259 - config.CONTRAST_ADJUSTMENT)); // This was established through experiments
|
|
||||||
var horizontalLineSpacing = Math.floor(height / config.LINE_COUNT); // Number of pixels to advance in vertical direction
|
|
||||||
|
|
||||||
// Iterate line by line (top line to bottom line) in increments of horizontalLineSpacing
|
|
||||||
for (var y = 0; y < height; y += horizontalLineSpacing) {
|
|
||||||
a = 0;
|
|
||||||
currentCoordinates = [];
|
|
||||||
currentCoordinates.push([0, y]); // Start the line
|
|
||||||
|
|
||||||
currentVerticalPixelIndex = y * width; // Because Image Pixel array is of length width * height,
|
|
||||||
// starting pixel for each line will be this
|
|
||||||
|
|
||||||
// Loop through pixels from left to right within the current line, advancing by increments of config.SPACING
|
|
||||||
for (var x = 1; x < width; x += config.SPACING) {
|
|
||||||
currentHorizontalPixelIndex = x + currentVerticalPixelIndex; // Get array position of current pixel
|
|
||||||
|
|
||||||
// When there is contrast adjustment, the calculations of brightness values are a bit different
|
|
||||||
if (config.CONTRAST_ADJUSTMENT !== 0) {
|
|
||||||
// Determine how bright a pixel is, from 0 to 255 by summing three channels (R,G,B) multiplied by some coefficients
|
|
||||||
b = 0.2125 * (contrastFactor * (imagePixels.data[4 * currentHorizontalPixelIndex] - 128) + 128 + config.BRIGHTNESS_ADJUSTMENT) + 0.7154 * (contrastFactor * (imagePixels.data[4 * (currentHorizontalPixelIndex + 1)] - 128) + 128 + config.BRIGHTNESS_ADJUSTMENT) + 0.0721 * (contrastFactor * (imagePixels.data[4 * (currentHorizontalPixelIndex + 2)] - 128) + 128 + config.BRIGHTNESS_ADJUSTMENT);
|
|
||||||
} else {
|
|
||||||
b = 0.2125 * (imagePixels.data[4 * currentHorizontalPixelIndex] + config.BRIGHTNESS_ADJUSTMENT) + 0.7154 * (imagePixels.data[4 * (currentHorizontalPixelIndex + 1)] + config.BRIGHTNESS_ADJUSTMENT) + 0.0721 * (imagePixels.data[4 * (currentHorizontalPixelIndex + 2)] + config.BRIGHTNESS_ADJUSTMENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
b = Math.max(config.MIN_BRIGHTNESS, b); // Set minimum line curvature to value set by the user
|
|
||||||
z = Math.max(config.MAX_BRIGHTNESS - b, 0); // Set maximum line curvature to value set by the user
|
|
||||||
|
|
||||||
// The magic of the script, determines how high / low the squiggle goes
|
|
||||||
r = config.AMPLITUDE * z / config.LINE_COUNT;
|
|
||||||
|
|
||||||
a += z / config.FREQUENCY;
|
|
||||||
currentCoordinates.push([x, y + Math.sin(a) * r]);
|
|
||||||
}
|
|
||||||
squiggleData.push(currentCoordinates);
|
|
||||||
}
|
|
||||||
self.postMessage(squiggleData);
|
|
||||||
}, false);
|
|
||||||
|
|
||||||
/***/ })
|
|
||||||
|
|
||||||
/******/ });
|
|
|
@ -1,65 +0,0 @@
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>SquiggleCam - capture a selfie, turn it into art</title>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
|
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes"/>
|
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="app.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<canvas id="canvas" width="800" height="600" style="display: none;"></canvas>
|
|
||||||
<div class="actions">
|
|
||||||
<div class="settings-container">
|
|
||||||
<div id="settings">
|
|
||||||
<div class="sliders">
|
|
||||||
<div class="col-50">
|
|
||||||
<label for="lineCount">Line Count (1...200):</label> <span id="lineCountValue">90</span>
|
|
||||||
<input id="lineCount" name="lineCount" type="range" min="1" max="200" value="90" step="1">
|
|
||||||
</div>
|
|
||||||
<div class="col-50">
|
|
||||||
<label for="amplitude">Amplitude (0.1...5): </label> <span id="amplitudeValue">1.2</span>
|
|
||||||
<input name="amplitude" id="amplitude" type="range" min="0.1" max="5" value="1.2" step="0.05">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="sliders">
|
|
||||||
<div class="col-50">
|
|
||||||
<label for="lineSpacing">Pixel Spacing (0.5...5):</label> <span id="lineSpacingValue">1</span>
|
|
||||||
<input id="lineSpacing" name="lineSpacing" type="range" min="0.5" max="5" value="1" step="0.5">
|
|
||||||
</div>
|
|
||||||
<div class="col-50">
|
|
||||||
<label for="frequency">Frequency (5...256): </label> <span id="frequencyValue">135</span>
|
|
||||||
<input name="frequency" id="frequency" type="range" min="5" max="256" value="135" step="1">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="sliders">
|
|
||||||
<div class="col-50">
|
|
||||||
<label for="brightness">Brightness (-100...100):</label> <span id="brightnessValue">0</span>
|
|
||||||
<input id="brightness" name="brightness" type="range" min="-100" max="100" value="0" step="1">
|
|
||||||
</div>
|
|
||||||
<div class="col-50">
|
|
||||||
<label for="contrast">Contrast (-100...100): </label> <span id="contrastValue">0</span>
|
|
||||||
<input id="contrast" name="contrast" type="range" min="-100" max="100" value="0" step="1">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="status-container">
|
|
||||||
<div id="status">
|
|
||||||
<video autoplay width="800" height="600"></video>
|
|
||||||
|
|
||||||
<a href="#" id="captureButton" class="btn btn-lg btn-success">Capture</a>
|
|
||||||
|
|
||||||
<a href="#" id="drawButton" class="btn btn-lg btn-primary" style="display: none;">Download</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div id="svg-placeholder"></div>
|
|
||||||
<script src="app.js"></script>
|
|
||||||
|
|
||||||
<script src="//localhost:35729/livereload.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -3,7 +3,11 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
<title>image-editor</title>
|
<title>SquiggleCam - capture a selfie, turn it into art</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes"/>
|
||||||
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
<link rel="stylesheet" href="https://maxcdn.icons8.com/fonts/line-awesome/1.1/css/line-awesome-font-awesome.min.css">
|
<link rel="stylesheet" href="https://maxcdn.icons8.com/fonts/line-awesome/1.1/css/line-awesome-font-awesome.min.css">
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
33
package.json
33
package.json
|
@ -1,43 +1,17 @@
|
||||||
{
|
{
|
||||||
<<<<<<< HEAD
|
|
||||||
"name": "SquiggleCam",
|
"name": "SquiggleCam",
|
||||||
"version": "1.0.0",
|
"version": "2.0.0",
|
||||||
"description": "Turn Images into squiggles for CNC machining",
|
"description": "Turn Images into squiggles for CNC machining",
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
|
||||||
"dev": "NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
|
|
||||||
"watch": "NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
|
|
||||||
"hot": "NODE_ENV=development webpack-dev-server --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
|
|
||||||
"production": "NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
|
|
||||||
},
|
|
||||||
"keywords": ["SquiggleDraw", "CNC", "SVG", "Laser Cutting"],
|
|
||||||
"author": "Maksim Surguy",
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"author": "Maksim <msurguy@gmail.com>",
|
||||||
"browser-sync": "^2.18.13",
|
"keywords": ["SquiggleDraw", "CNC", "SVG", "Laser Cutting"],
|
||||||
"browser-sync-webpack-plugin": "^1.2.0",
|
|
||||||
"laravel-mix": "^1.6.1",
|
|
||||||
"webpack-livereload-plugin": "^1.0.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"d3": "^4.11.0",
|
|
||||||
"rangeslider-js": "^3.0.2"
|
|
||||||
}
|
|
||||||
=======
|
|
||||||
"name": "image-editor",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "Image editor",
|
|
||||||
"author": "Maksim <msurguy@uw.edu>",
|
|
||||||
"private": true,
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
|
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
|
||||||
"start": "npm run dev",
|
"start": "npm run dev",
|
||||||
"build": "node build/build.js"
|
"build": "node build/build.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cropperjs": "^1.4.3",
|
|
||||||
"normalize.css": "^8.0.1",
|
"normalize.css": "^8.0.1",
|
||||||
"pica": "^5.0.0",
|
|
||||||
"vue": "^2.5.2",
|
"vue": "^2.5.2",
|
||||||
"vue-croppa": "^1.3.8",
|
"vue-croppa": "^1.3.8",
|
||||||
"vue-worker": "^1.2.1"
|
"vue-worker": "^1.2.1"
|
||||||
|
@ -90,5 +64,4 @@
|
||||||
"last 2 versions",
|
"last 2 versions",
|
||||||
"not ie <= 8"
|
"not ie <= 8"
|
||||||
]
|
]
|
||||||
>>>>>>> Initial commmit
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -201,8 +201,9 @@
|
||||||
|
|
||||||
<div class="credits">
|
<div class="credits">
|
||||||
<p>
|
<p>
|
||||||
Project by <a target="_blank" href="http://twitter.com/msurguy">@msurguy</a>
|
Project by <a target="_blank" href="http://twitter.com/msurguy">@msurguy</a> (<span class="fa fa-github"></span><a target="_blank" href="http://github.com/msurguy/SquiggleCam">Source</a>)
|
||||||
<br>Source on <span class="fa fa-github"></span><a href="http://github.com/msurguys/SquiggleCam">Github</a>
|
<br>Based upon
|
||||||
|
<a target="_blank" href="https://github.com/gwygonik/SquiggleDraw">SquiggleDraw</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,62 +0,0 @@
|
||||||
import * as d3 from "d3" // TODO: only import d3 modules that are needed for this script;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple class that takes point array and creates SVG lines connecting the points line by line
|
|
||||||
*/
|
|
||||||
export default class Renderer {
|
|
||||||
|
|
||||||
constructor(opts) {
|
|
||||||
// get data array and element reference
|
|
||||||
this.data = opts.data;
|
|
||||||
this.element = opts.element;
|
|
||||||
|
|
||||||
// Create an empty group that will store all lines
|
|
||||||
this.plot = this.element.append('g');
|
|
||||||
this.thickness = opts.thickness; // Set line thickness
|
|
||||||
this.addLines(); // Generate lines from array of points
|
|
||||||
}
|
|
||||||
|
|
||||||
addLines() {
|
|
||||||
// Create paths with array of points
|
|
||||||
let lines = this.plot.selectAll("path")
|
|
||||||
.data(this.data);
|
|
||||||
|
|
||||||
// Use first coordinate from data array point as X coordinate, second as Y coordinate
|
|
||||||
let drawLine = d3.line()
|
|
||||||
.x(function(d) {
|
|
||||||
return d[0];
|
|
||||||
})
|
|
||||||
.y(function(d) {
|
|
||||||
return d[1];
|
|
||||||
});
|
|
||||||
//.curve(d3.curveNatural); // You can try setting some curvature for the
|
|
||||||
// generated lines (see http://bl.ocks.org/d3indepth/b6d4845973089bc1012dec1674d3aff8)
|
|
||||||
|
|
||||||
// update the paths to reflect the latest data in the data array
|
|
||||||
lines.enter().append("path")
|
|
||||||
.attr("stroke", "black")
|
|
||||||
.attr("stroke-width", this.thickness + "px")
|
|
||||||
.attr("fill", "none")
|
|
||||||
.merge(lines)
|
|
||||||
.attr("d", (d) => {
|
|
||||||
return drawLine(d)
|
|
||||||
});
|
|
||||||
// remove old lines from previous dataset
|
|
||||||
lines.exit().remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
// setThickness(thickness) {
|
|
||||||
// this.thickness = thickness;
|
|
||||||
// this.plot.selectAll("path")
|
|
||||||
// .attr("stroke-width", this.thickness + "px");
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* sets local data array to new array and updates the lines
|
|
||||||
* @param newData
|
|
||||||
*/
|
|
||||||
setData(newData) {
|
|
||||||
this.data = newData;
|
|
||||||
this.addLines();
|
|
||||||
}
|
|
||||||
}
|
|
214
src/app.js
214
src/app.js
|
@ -1,214 +0,0 @@
|
||||||
import * as d3 from "d3";
|
|
||||||
import Renderer from "./Renderer";
|
|
||||||
import rangesliderJs from 'rangeslider-js';
|
|
||||||
|
|
||||||
let worker = new Worker('imageAnalyzer.js');
|
|
||||||
let imagePixels = [];
|
|
||||||
|
|
||||||
// get the canvas and video elements
|
|
||||||
const canvas = document.getElementById("canvas");
|
|
||||||
const context = canvas.getContext('2d');
|
|
||||||
|
|
||||||
const video = document.querySelector('video');
|
|
||||||
|
|
||||||
// HTML Elements for the sliders
|
|
||||||
const lineCountValueEl = document.getElementById('lineCountValue');
|
|
||||||
const amplitudeValueEl = document.getElementById('amplitudeValue');
|
|
||||||
const lineSpacingValueEl = document.getElementById('lineSpacingValue');
|
|
||||||
const frequencyValueEl = document.getElementById('frequencyValue');
|
|
||||||
const brightnessValueEl = document.getElementById('brightnessValue');
|
|
||||||
const contrastValueEl = document.getElementById('contrastValue');
|
|
||||||
const lineCountValue = document.getElementById('lineCount');
|
|
||||||
const amplitudeValue = document.getElementById('amplitude');
|
|
||||||
const lineSpacingValue = document.getElementById('lineSpacing');
|
|
||||||
const frequencyValue = document.getElementById('frequency');
|
|
||||||
const brightnessValue = document.getElementById('brightness');
|
|
||||||
const contrastValue = document.getElementById('contrast');
|
|
||||||
const captureButton = document.getElementById('captureButton');
|
|
||||||
const drawButton = document.getElementById('drawButton');
|
|
||||||
|
|
||||||
const config = {
|
|
||||||
LINE_COUNT: 90,
|
|
||||||
AMPLITUDE: 1.2,
|
|
||||||
SPACING: 1,
|
|
||||||
FREQUENCY: 135,
|
|
||||||
LINE_WIDTH: 2,
|
|
||||||
BRIGHTNESS_ADJUSTMENT: 0,
|
|
||||||
CONTRAST_ADJUSTMENT: 0,
|
|
||||||
MIN_BRIGHTNESS : 0,
|
|
||||||
MAX_BRIGHTNESS: 255,
|
|
||||||
WIDTH: 550, // Resulting SVG width, in pixels
|
|
||||||
HEIGHT: 550 // Resulting SVG height, in pixels
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create empty SVG structure
|
|
||||||
const svgContainer = d3.select("#svg-placeholder").append("svg")
|
|
||||||
.attr("version", 1.1)
|
|
||||||
.attr("width", config.WIDTH)
|
|
||||||
.attr("height", config.HEIGHT)
|
|
||||||
.attr("viewBox", "0 0 "+ config.WIDTH + " "+config.HEIGHT)
|
|
||||||
.attr("xmlns", "http://www.w3.org/2000/svg");
|
|
||||||
|
|
||||||
// Create renderer instance
|
|
||||||
const renderer = new Renderer({
|
|
||||||
element: svgContainer,
|
|
||||||
data: [],
|
|
||||||
thickness : config.LINE_WIDTH
|
|
||||||
});
|
|
||||||
|
|
||||||
// Define what kind of media you want to acquire (type, resolution, etc)
|
|
||||||
// according to getUserMedia API
|
|
||||||
const mediaConstraints = {
|
|
||||||
audio: false,
|
|
||||||
video: {
|
|
||||||
width: config.WIDTH,
|
|
||||||
aspectRatio: {
|
|
||||||
exact: config.WIDTH / config.HEIGHT
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Capture media from Webcam
|
|
||||||
navigator.mediaDevices.getUserMedia(mediaConstraints)
|
|
||||||
.then((mediaStream) => {
|
|
||||||
video.srcObject = mediaStream;
|
|
||||||
video.onloadedmetadata = function(e) {
|
|
||||||
video.play();
|
|
||||||
};
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.log(err.name + ": " + err.message);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// When worker posts a message, process it
|
|
||||||
worker.addEventListener('message', function(e) {
|
|
||||||
renderer.setData(e.data); // pass the received data to the renderer instance
|
|
||||||
}, false);
|
|
||||||
|
|
||||||
worker.onerror = function(error) {
|
|
||||||
console.log('Worker error: ' + error.message + '\n');
|
|
||||||
throw error;
|
|
||||||
};
|
|
||||||
|
|
||||||
// When image needs to be turned into squiggles, we need to post the image pixel data
|
|
||||||
// and the config to the worker
|
|
||||||
function processImage() {
|
|
||||||
worker.postMessage({ data: imagePixels, config: config});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attach click even to "Capture" button
|
|
||||||
captureButton.addEventListener('click', () => {
|
|
||||||
drawButton.style.display = 'inline-block';
|
|
||||||
|
|
||||||
// Set the video and canvas width / height to the target size of our SVG
|
|
||||||
video.setAttribute('width', config.WIDTH);
|
|
||||||
video.setAttribute('height', config.HEIGHT);
|
|
||||||
canvas.width = config.WIDTH;
|
|
||||||
canvas.height = config.HEIGHT;
|
|
||||||
// Clear the canvas
|
|
||||||
context.clearRect(0, 0, config.WIDTH, config.HEIGHT);
|
|
||||||
context.drawImage(video, 0, 0, config.WIDTH, config.HEIGHT);
|
|
||||||
// Retrieve all pixels from the image
|
|
||||||
imagePixels = context.getImageData(0, 0, config.WIDTH, config.HEIGHT);
|
|
||||||
// Send the pixels to the worker
|
|
||||||
processImage();
|
|
||||||
});
|
|
||||||
|
|
||||||
drawButton.addEventListener('click', () => {
|
|
||||||
drawButton.classList.add('disabled');
|
|
||||||
|
|
||||||
const svgDoctype = '<?xml version="1.0" standalone="no"?>'
|
|
||||||
+ '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">';
|
|
||||||
|
|
||||||
// serialize our SVG XML to a string.
|
|
||||||
let svgString = (new XMLSerializer()).serializeToString(d3.select('svg').node());
|
|
||||||
|
|
||||||
// reduce the SVG path by cutting off floating point values after the first digit beyond floating point (~50% less MBs)
|
|
||||||
svgString = svgString.replace(/([\-+]?\d{1,}\.\d{3,}([eE][\-+]?\d+)?)/g, function (x) {
|
|
||||||
return (+x).toFixed(1)
|
|
||||||
});
|
|
||||||
|
|
||||||
const blob = new Blob([svgDoctype + svgString], {type: 'image/svg+xml;charset=utf-8'});
|
|
||||||
|
|
||||||
/* This portion of script saves the file to local filesystem as a download */
|
|
||||||
const svgUrl = URL.createObjectURL(blob);
|
|
||||||
|
|
||||||
const downloadLink = document.createElement("a");
|
|
||||||
downloadLink.href = svgUrl;
|
|
||||||
downloadLink.download = "squiggleCam_" + Date.now() + ".svg";
|
|
||||||
document.body.appendChild(downloadLink);
|
|
||||||
downloadLink.click();
|
|
||||||
document.body.removeChild(downloadLink);
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
// If you wanted to upload the image to a server instead, use something like this:
|
|
||||||
|
|
||||||
const oReq = new XMLHttpRequest();
|
|
||||||
oReq.open("POST", serverIPAddress + ':3000/upload', true);
|
|
||||||
oReq.onload = function (oEvent) {
|
|
||||||
console.log('uploaded!', oEvent);
|
|
||||||
};
|
|
||||||
|
|
||||||
oReq.onreadystatechange = () => {
|
|
||||||
if (oReq.readyState === 4 && oReq.status === 200) {
|
|
||||||
drawButton.classList.remove('disabled');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
oReq.send(blob);*/
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// Attach listener to each of the range sliders
|
|
||||||
rangesliderJs.create(lineCountValue, {
|
|
||||||
onSlide: (val) => {
|
|
||||||
lineCountValueEl.innerHTML = val;
|
|
||||||
config.LINE_COUNT = val;
|
|
||||||
processImage();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
rangesliderJs.create(amplitudeValue, {
|
|
||||||
onSlide: (val) => {
|
|
||||||
amplitudeValueEl.innerHTML = val;
|
|
||||||
config.AMPLITUDE = val;
|
|
||||||
processImage();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
rangesliderJs.create(lineSpacingValue, {
|
|
||||||
onSlide: (val) => {
|
|
||||||
lineSpacingValueEl.innerHTML = val;
|
|
||||||
config.SPACING = val;
|
|
||||||
processImage();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
rangesliderJs.create(frequencyValue, {
|
|
||||||
onSlide: (val) => {
|
|
||||||
frequencyValueEl.innerHTML = val;
|
|
||||||
config.FREQUENCY = val;
|
|
||||||
processImage();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
rangesliderJs.create(brightnessValue, {
|
|
||||||
onSlide: (val) => {
|
|
||||||
brightnessValueEl.innerHTML = val;
|
|
||||||
config.BRIGHTNESS_ADJUSTMENT = val;
|
|
||||||
processImage();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
rangesliderJs.create(contrastValue, {
|
|
||||||
onSlide: (val) => {
|
|
||||||
contrastValueEl.innerHTML = val;
|
|
||||||
config.CONTRAST_ADJUSTMENT = val;
|
|
||||||
processImage();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
@import "styles/main";
|
|
||||||
@import "styles/rangeslider";
|
|
||||||
@import "styles/buttons";
|
|
|
@ -1,72 +0,0 @@
|
||||||
/**
|
|
||||||
* imageAnalyzer Web Worker accepts image data and configuration parameters
|
|
||||||
* and returns array of x,y coordinates to draw squiggles
|
|
||||||
*
|
|
||||||
* It analyzes pixels for their average brightness and if brightness is closer to 0 -> makes lines less
|
|
||||||
* squiggly, if closer to 255 -> more squiggly
|
|
||||||
*
|
|
||||||
* This script runs in a webWorker thread in order to speed up processing of the image and
|
|
||||||
* to detach the processing from the UI updates
|
|
||||||
*
|
|
||||||
* Majority of the logic in this script is taken from https://github.com/gwygonik/SquiggleDraw for the following exceptions:
|
|
||||||
* - I implemented contrast adjustment that can be helpful in certain light conditions
|
|
||||||
* - I increased the processing speed by removing unnecessary assignments and moving variable
|
|
||||||
* declarations out of the processing loop
|
|
||||||
*/
|
|
||||||
|
|
||||||
self.addEventListener('message', function(e) {
|
|
||||||
// Gather all necessary data from the main thread
|
|
||||||
let config = e.data.config;
|
|
||||||
let imagePixels = e.data.data;
|
|
||||||
let width = e.data.config.WIDTH;
|
|
||||||
let height = e.data.config.HEIGHT;
|
|
||||||
|
|
||||||
// Create some defaults for squiggle-point array
|
|
||||||
let squiggleData = [];
|
|
||||||
let r = 5;
|
|
||||||
let a = 0;
|
|
||||||
let b;
|
|
||||||
let z;
|
|
||||||
let currentCoordinates = []; // create empty array for storing x,y coordinate pairs
|
|
||||||
let currentVerticalPixelIndex = 0;
|
|
||||||
let currentHorizontalPixelIndex = 0;
|
|
||||||
let contrastFactor = (259 * (config.CONTRAST_ADJUSTMENT + 255)) / (255 * (259 - config.CONTRAST_ADJUSTMENT)); // This was established through experiments
|
|
||||||
let horizontalLineSpacing = Math.floor(height/config.LINE_COUNT); // Number of pixels to advance in vertical direction
|
|
||||||
|
|
||||||
// Iterate line by line (top line to bottom line) in increments of horizontalLineSpacing
|
|
||||||
for (let y = 0; y < height; y+= horizontalLineSpacing) {
|
|
||||||
a = 0;
|
|
||||||
currentCoordinates = [];
|
|
||||||
currentCoordinates.push([0, y]); // Start the line
|
|
||||||
|
|
||||||
currentVerticalPixelIndex = y*width; // Because Image Pixel array is of length width * height,
|
|
||||||
// starting pixel for each line will be this
|
|
||||||
|
|
||||||
// Loop through pixels from left to right within the current line, advancing by increments of config.SPACING
|
|
||||||
for (let x = 1;x < width; x += config.SPACING ) {
|
|
||||||
currentHorizontalPixelIndex = x + currentVerticalPixelIndex; // Get array position of current pixel
|
|
||||||
|
|
||||||
// When there is contrast adjustment, the calculations of brightness values are a bit different
|
|
||||||
if (config.CONTRAST_ADJUSTMENT !== 0) {
|
|
||||||
// Determine how bright a pixel is, from 0 to 255 by summing three channels (R,G,B) multiplied by some coefficients
|
|
||||||
b = (0.2125 * ((contrastFactor * (imagePixels.data[4 * currentHorizontalPixelIndex] - 128) + 128 )
|
|
||||||
+ config.BRIGHTNESS_ADJUSTMENT)) + (0.7154 * ((contrastFactor * (imagePixels.data[4 * (currentHorizontalPixelIndex + 1)] - 128) + 128)
|
|
||||||
+ config.BRIGHTNESS_ADJUSTMENT)) + (0.0721 * ((contrastFactor*(imagePixels.data[4*(currentHorizontalPixelIndex+2)]-128)+128) + config.BRIGHTNESS_ADJUSTMENT));
|
|
||||||
} else {
|
|
||||||
b = (0.2125 * (imagePixels.data[4*currentHorizontalPixelIndex] + config.BRIGHTNESS_ADJUSTMENT)) + (0.7154 * (imagePixels.data[4*(currentHorizontalPixelIndex + 1)]+ config.BRIGHTNESS_ADJUSTMENT)) + (0.0721 * (imagePixels.data[4*(currentHorizontalPixelIndex + 2)] + config.BRIGHTNESS_ADJUSTMENT));
|
|
||||||
}
|
|
||||||
|
|
||||||
b = Math.max(config.MIN_BRIGHTNESS,b); // Set minimum line curvature to value set by the user
|
|
||||||
z = Math.max(config.MAX_BRIGHTNESS-b,0); // Set maximum line curvature to value set by the user
|
|
||||||
|
|
||||||
// The magic of the script, determines how high / low the squiggle goes
|
|
||||||
r = config.AMPLITUDE * z / config.LINE_COUNT;
|
|
||||||
|
|
||||||
a += z / config.FREQUENCY;
|
|
||||||
currentCoordinates.push([x,y + Math.sin(a)*r]);
|
|
||||||
}
|
|
||||||
squiggleData.push(currentCoordinates);
|
|
||||||
}
|
|
||||||
self.postMessage(squiggleData);
|
|
||||||
|
|
||||||
}, false);
|
|
|
@ -1,65 +0,0 @@
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>SquiggleCam - capture a selfie, turn it into art</title>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
|
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes"/>
|
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="app.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<canvas id="canvas" width="800" height="600" style="display: none;"></canvas>
|
|
||||||
<div class="actions">
|
|
||||||
<div class="settings-container">
|
|
||||||
<div id="settings">
|
|
||||||
<div class="sliders">
|
|
||||||
<div class="col-50">
|
|
||||||
<label for="lineCount">Line Count (1...200):</label> <span id="lineCountValue">90</span>
|
|
||||||
<input id="lineCount" name="lineCount" type="range" min="1" max="200" value="90" step="1">
|
|
||||||
</div>
|
|
||||||
<div class="col-50">
|
|
||||||
<label for="amplitude">Amplitude (0.1...5): </label> <span id="amplitudeValue">1.2</span>
|
|
||||||
<input name="amplitude" id="amplitude" type="range" min="0.1" max="5" value="1.2" step="0.05">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="sliders">
|
|
||||||
<div class="col-50">
|
|
||||||
<label for="lineSpacing">Pixel Spacing (0.5...5):</label> <span id="lineSpacingValue">1</span>
|
|
||||||
<input id="lineSpacing" name="lineSpacing" type="range" min="0.5" max="5" value="1" step="0.5">
|
|
||||||
</div>
|
|
||||||
<div class="col-50">
|
|
||||||
<label for="frequency">Frequency (5...256): </label> <span id="frequencyValue">135</span>
|
|
||||||
<input name="frequency" id="frequency" type="range" min="5" max="256" value="135" step="1">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="sliders">
|
|
||||||
<div class="col-50">
|
|
||||||
<label for="brightness">Brightness (-100...100):</label> <span id="brightnessValue">0</span>
|
|
||||||
<input id="brightness" name="brightness" type="range" min="-100" max="100" value="0" step="1">
|
|
||||||
</div>
|
|
||||||
<div class="col-50">
|
|
||||||
<label for="contrast">Contrast (-100...100): </label> <span id="contrastValue">0</span>
|
|
||||||
<input id="contrast" name="contrast" type="range" min="-100" max="100" value="0" step="1">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="status-container">
|
|
||||||
<div id="status">
|
|
||||||
<video autoplay width="800" height="600"></video>
|
|
||||||
|
|
||||||
<a href="#" id="captureButton" class="btn btn-lg btn-success">Capture</a>
|
|
||||||
|
|
||||||
<a href="#" id="drawButton" class="btn btn-lg btn-primary" style="display: none;">Get SVG!</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div id="svg-placeholder"></div>
|
|
||||||
<script src="app.js"></script>
|
|
||||||
|
|
||||||
<script src="//localhost:35729/livereload.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
12
src/lib.js
12
src/lib.js
|
@ -1,12 +0,0 @@
|
||||||
export function map(value, inMin, inMax, outMin, outMax) {
|
|
||||||
return (value - inMin) * (outMax - outMin) / (inMax - inMin) + outMin
|
|
||||||
}
|
|
||||||
|
|
||||||
export function range(start, end, tick) {
|
|
||||||
const s = Math.round(start / tick) * tick
|
|
||||||
return Array.from({
|
|
||||||
length: Math.floor((end - start) / tick)
|
|
||||||
}, (v, k) => {
|
|
||||||
return k * tick + s
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,302 +0,0 @@
|
||||||
|
|
||||||
.btn {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 8px 8px;
|
|
||||||
margin-bottom: 0;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: normal;
|
|
||||||
line-height: 1.42857143;
|
|
||||||
text-align: center;
|
|
||||||
white-space: nowrap;
|
|
||||||
vertical-align: middle;
|
|
||||||
-ms-touch-action: manipulation;
|
|
||||||
touch-action: manipulation;
|
|
||||||
cursor: pointer;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-moz-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
background-image: none;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
//border-radius: 10px;
|
|
||||||
}
|
|
||||||
.btn:focus,
|
|
||||||
.btn:active:focus,
|
|
||||||
.btn.active:focus,
|
|
||||||
.btn.focus,
|
|
||||||
.btn:active.focus,
|
|
||||||
.btn.active.focus {
|
|
||||||
outline: thin dotted;
|
|
||||||
outline: 5px auto -webkit-focus-ring-color;
|
|
||||||
outline-offset: -2px;
|
|
||||||
}
|
|
||||||
.btn:hover,
|
|
||||||
.btn:focus,
|
|
||||||
.btn.focus {
|
|
||||||
color: #333;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
.btn:active,
|
|
||||||
.btn.active {
|
|
||||||
background-image: none;
|
|
||||||
outline: 0;
|
|
||||||
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
|
|
||||||
box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
|
|
||||||
}
|
|
||||||
.btn.disabled,
|
|
||||||
.btn[disabled],
|
|
||||||
fieldset[disabled] .btn {
|
|
||||||
cursor: not-allowed;
|
|
||||||
filter: alpha(opacity=65);
|
|
||||||
-webkit-box-shadow: none;
|
|
||||||
box-shadow: none;
|
|
||||||
opacity: .65;
|
|
||||||
}
|
|
||||||
a.btn.disabled,
|
|
||||||
fieldset[disabled] a.btn {
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #337ab7;
|
|
||||||
border-color: #2e6da4;
|
|
||||||
}
|
|
||||||
.btn-primary:focus,
|
|
||||||
.btn-primary.focus {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #286090;
|
|
||||||
border-color: #122b40;
|
|
||||||
}
|
|
||||||
.btn-primary:hover {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #286090;
|
|
||||||
border-color: #204d74;
|
|
||||||
}
|
|
||||||
.btn-primary:active,
|
|
||||||
.btn-primary.active,
|
|
||||||
.open > .dropdown-toggle.btn-primary {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #286090;
|
|
||||||
border-color: #204d74;
|
|
||||||
}
|
|
||||||
.btn-primary:active:hover,
|
|
||||||
.btn-primary.active:hover,
|
|
||||||
.open > .dropdown-toggle.btn-primary:hover,
|
|
||||||
.btn-primary:active:focus,
|
|
||||||
.btn-primary.active:focus,
|
|
||||||
.open > .dropdown-toggle.btn-primary:focus,
|
|
||||||
.btn-primary:active.focus,
|
|
||||||
.btn-primary.active.focus,
|
|
||||||
.open > .dropdown-toggle.btn-primary.focus {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #204d74;
|
|
||||||
border-color: #122b40;
|
|
||||||
}
|
|
||||||
.btn-primary:active,
|
|
||||||
.btn-primary.active,
|
|
||||||
.open > .dropdown-toggle.btn-primary {
|
|
||||||
background-image: none;
|
|
||||||
}
|
|
||||||
.btn-primary.disabled:hover,
|
|
||||||
.btn-primary[disabled]:hover,
|
|
||||||
fieldset[disabled] .btn-primary:hover,
|
|
||||||
.btn-primary.disabled:focus,
|
|
||||||
.btn-primary[disabled]:focus,
|
|
||||||
fieldset[disabled] .btn-primary:focus,
|
|
||||||
.btn-primary.disabled.focus,
|
|
||||||
.btn-primary[disabled].focus,
|
|
||||||
fieldset[disabled] .btn-primary.focus {
|
|
||||||
background-color: #337ab7;
|
|
||||||
border-color: #2e6da4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-success {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #5cb85c;
|
|
||||||
border-color: #4cae4c;
|
|
||||||
}
|
|
||||||
.btn-success:focus,
|
|
||||||
.btn-success.focus {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #449d44;
|
|
||||||
border-color: #255625;
|
|
||||||
}
|
|
||||||
.btn-success:hover {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #449d44;
|
|
||||||
border-color: #398439;
|
|
||||||
}
|
|
||||||
.btn-success:active,
|
|
||||||
.btn-success.active,
|
|
||||||
.open > .dropdown-toggle.btn-success {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #449d44;
|
|
||||||
border-color: #398439;
|
|
||||||
}
|
|
||||||
.btn-success:active:hover,
|
|
||||||
.btn-success.active:hover,
|
|
||||||
.open > .dropdown-toggle.btn-success:hover,
|
|
||||||
.btn-success:active:focus,
|
|
||||||
.btn-success.active:focus,
|
|
||||||
.open > .dropdown-toggle.btn-success:focus,
|
|
||||||
.btn-success:active.focus,
|
|
||||||
.btn-success.active.focus,
|
|
||||||
.open > .dropdown-toggle.btn-success.focus {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #398439;
|
|
||||||
border-color: #255625;
|
|
||||||
}
|
|
||||||
.btn-success:active,
|
|
||||||
.btn-success.active,
|
|
||||||
.open > .dropdown-toggle.btn-success {
|
|
||||||
background-image: none;
|
|
||||||
}
|
|
||||||
.btn-success.disabled:hover,
|
|
||||||
.btn-success[disabled]:hover,
|
|
||||||
fieldset[disabled] .btn-success:hover,
|
|
||||||
.btn-success.disabled:focus,
|
|
||||||
.btn-success[disabled]:focus,
|
|
||||||
fieldset[disabled] .btn-success:focus,
|
|
||||||
.btn-success.disabled.focus,
|
|
||||||
.btn-success[disabled].focus,
|
|
||||||
fieldset[disabled] .btn-success.focus {
|
|
||||||
background-color: #5cb85c;
|
|
||||||
border-color: #4cae4c;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-info {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #5bc0de;
|
|
||||||
border-color: #46b8da;
|
|
||||||
}
|
|
||||||
.btn-info:focus,
|
|
||||||
.btn-info.focus {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #31b0d5;
|
|
||||||
border-color: #1b6d85;
|
|
||||||
}
|
|
||||||
.btn-info:hover {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #31b0d5;
|
|
||||||
border-color: #269abc;
|
|
||||||
}
|
|
||||||
.btn-info:active,
|
|
||||||
.btn-info.active,
|
|
||||||
.open > .dropdown-toggle.btn-info {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #31b0d5;
|
|
||||||
border-color: #269abc;
|
|
||||||
}
|
|
||||||
.btn-info:active:hover,
|
|
||||||
.btn-info.active:hover,
|
|
||||||
.open > .dropdown-toggle.btn-info:hover,
|
|
||||||
.btn-info:active:focus,
|
|
||||||
.btn-info.active:focus,
|
|
||||||
.open > .dropdown-toggle.btn-info:focus,
|
|
||||||
.btn-info:active.focus,
|
|
||||||
.btn-info.active.focus,
|
|
||||||
.open > .dropdown-toggle.btn-info.focus {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #269abc;
|
|
||||||
border-color: #1b6d85;
|
|
||||||
}
|
|
||||||
.btn-info:active,
|
|
||||||
.btn-info.active,
|
|
||||||
.open > .dropdown-toggle.btn-info {
|
|
||||||
background-image: none;
|
|
||||||
}
|
|
||||||
.btn-info.disabled:hover,
|
|
||||||
.btn-info[disabled]:hover,
|
|
||||||
fieldset[disabled] .btn-info:hover,
|
|
||||||
.btn-info.disabled:focus,
|
|
||||||
.btn-info[disabled]:focus,
|
|
||||||
fieldset[disabled] .btn-info:focus,
|
|
||||||
.btn-info.disabled.focus,
|
|
||||||
.btn-info[disabled].focus,
|
|
||||||
fieldset[disabled] .btn-info.focus {
|
|
||||||
background-color: #5bc0de;
|
|
||||||
border-color: #46b8da;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-danger {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #d9534f;
|
|
||||||
border-color: #d43f3a;
|
|
||||||
}
|
|
||||||
.btn-danger:focus,
|
|
||||||
.btn-danger.focus {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #c9302c;
|
|
||||||
border-color: #761c19;
|
|
||||||
}
|
|
||||||
.btn-danger:hover {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #c9302c;
|
|
||||||
border-color: #ac2925;
|
|
||||||
}
|
|
||||||
.btn-danger:active,
|
|
||||||
.btn-danger.active,
|
|
||||||
.open > .dropdown-toggle.btn-danger {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #c9302c;
|
|
||||||
border-color: #ac2925;
|
|
||||||
}
|
|
||||||
.btn-danger:active:hover,
|
|
||||||
.btn-danger.active:hover,
|
|
||||||
.open > .dropdown-toggle.btn-danger:hover,
|
|
||||||
.btn-danger:active:focus,
|
|
||||||
.btn-danger.active:focus,
|
|
||||||
.open > .dropdown-toggle.btn-danger:focus,
|
|
||||||
.btn-danger:active.focus,
|
|
||||||
.btn-danger.active.focus,
|
|
||||||
.open > .dropdown-toggle.btn-danger.focus {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #ac2925;
|
|
||||||
border-color: #761c19;
|
|
||||||
}
|
|
||||||
.btn-danger:active,
|
|
||||||
.btn-danger.active,
|
|
||||||
.open > .dropdown-toggle.btn-danger {
|
|
||||||
background-image: none;
|
|
||||||
}
|
|
||||||
.btn-danger.disabled:hover,
|
|
||||||
.btn-danger[disabled]:hover,
|
|
||||||
fieldset[disabled] .btn-danger:hover,
|
|
||||||
.btn-danger.disabled:focus,
|
|
||||||
.btn-danger[disabled]:focus,
|
|
||||||
fieldset[disabled] .btn-danger:focus,
|
|
||||||
.btn-danger.disabled.focus,
|
|
||||||
.btn-danger[disabled].focus,
|
|
||||||
fieldset[disabled] .btn-danger.focus {
|
|
||||||
background-color: #d9534f;
|
|
||||||
border-color: #d43f3a;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
a:active,
|
|
||||||
a:hover {
|
|
||||||
outline: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #337ab7;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
a:hover,
|
|
||||||
a:focus {
|
|
||||||
color: #23527c;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
a:focus {
|
|
||||||
outline: thin dotted;
|
|
||||||
outline: 5px auto -webkit-focus-ring-color;
|
|
||||||
outline-offset: -2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-block {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
|
@ -1,70 +0,0 @@
|
||||||
body{
|
|
||||||
width: 100%;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 1.42857143;
|
|
||||||
color: #333;
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
svg {
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
video {
|
|
||||||
width: 100px;
|
|
||||||
height: 100px;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings-container {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-container {
|
|
||||||
flex: 0 0 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#status {
|
|
||||||
display: inline-block;
|
|
||||||
width: 100px;
|
|
||||||
text-align: center;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions {
|
|
||||||
background-color: black;
|
|
||||||
color: white;
|
|
||||||
z-index: 100;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 210px;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sliders {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
flex-direction: row;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col-50 {
|
|
||||||
position: relative;
|
|
||||||
margin: 0;
|
|
||||||
padding: 10px;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#drawButton {
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
.rangeslider {
|
|
||||||
position: relative;
|
|
||||||
display: block;
|
|
||||||
cursor: pointer;
|
|
||||||
height: 25px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.rangeslider__fill,
|
|
||||||
.rangeslider__fill__bg {
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
height: 2px;
|
|
||||||
z-index: 2;
|
|
||||||
background: #29e;
|
|
||||||
border-radius: 10px;
|
|
||||||
will-change: width;
|
|
||||||
}
|
|
||||||
.rangeslider__handle {
|
|
||||||
will-change: width, height, top;
|
|
||||||
width: 18px;
|
|
||||||
height: 18px;
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
background: #29e;
|
|
||||||
display: inline-block;
|
|
||||||
z-index: 3;
|
|
||||||
cursor: pointer;
|
|
||||||
border: solid 2px #ffffff;
|
|
||||||
border-radius: 50%;
|
|
||||||
-webkit-transition: width 0.1s ease-in-out, height 0.1s ease-in-out, top 0.1s ease-in-out;
|
|
||||||
transition: width 0.1s ease-in-out, height 0.1s ease-in-out, top 0.1s ease-in-out;
|
|
||||||
}
|
|
||||||
.rangeslider__handle:active {
|
|
||||||
background: #107ecd;
|
|
||||||
}
|
|
||||||
.rangeslider__fill__bg {
|
|
||||||
background: #ccc;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.rangeslider--disabled {
|
|
||||||
opacity: 0.4;
|
|
||||||
}
|
|
|
@ -375,6 +375,9 @@ input[type="range"]:focus::-ms-fill-upper {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
h2 {
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-controls {
|
.video-controls {
|
||||||
|
|
Ładowanie…
Reference in New Issue