added brightness and width / height settings

todo: add watchers for brightness, hide the controls somewhere
master
Maksim 2019-01-28 01:31:36 -08:00
rodzic b9ed1d6196
commit 35c849dcb9
8 zmienionych plików z 68 dodań i 642 usunięć

Wyświetl plik

@ -11,8 +11,8 @@
<image-chooser @selected="onInputSelected"></image-chooser>
<div class="image-upload" v-if="this.inputType === 'upload'">
<croppa v-model="myCroppa" :width="250" :height="250" :preventWhiteSpace="true" :quality="2"></croppa>
<button @click="uploadCroppedImage">Output JPEG</button>
<croppa v-model="myCroppa" :width="settings.width/2" :height="settings.height/2" :preventWhiteSpace="true" :quality="2"></croppa>
<button class="btn btn-primary btn-block" style="display:block;" @click="uploadCroppedImage">Use Image</button>
</div>
<div class="image-webcam" v-if="this.inputType === 'webcam'">
@ -52,13 +52,44 @@
<circle cx="24" cy="24" r="23" stroke="white" stroke-width="1" fill="#D41616" />
</svg>
</button>
</div>
<div class="section-title">
Squiggle Controls:
</div>
<div class="slider">
<span class="label">
Width
</span>
<input type="range" min="200" max="500" v-model="settings.width">
<div class="output">{{ settings.width }}</div>
</div>
<div class="slider">
<span class="label">
Height
</span>
<input type="range" min="200" max="500" v-model="settings.height">
<div class="output">{{ settings.height }}</div>
</div>
<div class="slider">
<span class="label">
Min brightness
</span>
<input type="range" min="0" max="255" v-model="settings.minBrightness">
<div class="output">{{ settings.minBrightness }}</div>
</div>
<div class="slider">
<span class="label">
Max brightness
</span>
<input type="range" min="0" max="255" v-model="settings.maxBrightness">
<div class="output">{{ settings.maxBrightness }}</div>
</div>
<div class="slider">
<span class="label">
Frequency
@ -101,15 +132,10 @@
Download:
</div>
<div class="actions">
<button class="btn">
JPG
</button>
<button class="btn" @click="downloadSVG">
SVG
</button>
<button class="btn">
ZIP
</button>
</div>
</aside>
@ -124,9 +150,6 @@
</template>
<script>
// import Navbar from './components/Navbar'
// import Loader from './components/Loader'
// import Editor from './components/Editor'
import ImageChooser from './components/ImageChooser'
import WebCam from './components/WebCam'
import svgChart from './components/svgChart';
@ -134,9 +157,6 @@
export default {
name: 'App',
components: {
//cropActions: Navbar ,
//loader: Loader,
//editor: Editor,
imageChooser: ImageChooser,
webcam: WebCam,
svgChart: svgChart
@ -172,16 +192,6 @@
devices: [],
streaming: false
},
// cropper: {
// cropped: false,
// cropping: false,
// loaded: false,
// name: '',
// previousUrl: '',
// type: '',
// url: '',
// croppedImageData: ''
// },
};
},
@ -197,12 +207,6 @@
this.webcam.deviceId = first.deviceId;
}
},
// 'cropper.croppedImageData': function(){
// const canvas = this.cropper.croppedImageData;
// const ctx = canvas.getContext("2d");
// this.canvasData = ctx.getImageData(0, 0, 500, 500);
// return true;
// },
'settings.frequency': function(){
this.processImage();
},
@ -219,13 +223,27 @@
methods: {
downloadSVG(){
console.log(this.$refs.svgResult.$el.innerHTML);
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.
const svgString = (new XMLSerializer()).serializeToString(this.$refs.svgResult.$el);
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);
},
uploadCroppedImage() {
this.myCroppa.generateBlob((blob) => {
let canvas = document.createElement("canvas");
canvas.width = 500;
canvas.height = 500;
canvas.width = this.settings.width;
canvas.height = this.settings.height;
// const ctx = canvas.getContext("2d");
// this.canvasData = ctx.getImageData(0, 0, 500, 500);
@ -234,7 +252,7 @@
img.onload = () => {
ctx.drawImage(img, 0, 0)
this.canvasData = ctx.getImageData(0, 0, 500, 500);
this.canvasData = ctx.getImageData(0, 0, this.settings.width, this.settings.height);
};
img.src = URL.createObjectURL(blob);
@ -246,8 +264,8 @@
let config = data.config;
// context.getImageData(0, 0, config.WIDTH, config.HEIGHT);
let imagePixels = data.image;
let width = config.width;
let height = config.height;
let width = parseInt(config.width);
let height = parseInt(config.height);
// Create some defaults for squiggle-point array
let squiggleData = [];
@ -259,7 +277,7 @@
let currentVerticalPixelIndex = 0;
let currentHorizontalPixelIndex = 0;
let contrastFactor = (259 * (config.contrast + 255)) / (255 * (259 - config.contrast)); // This was established through experiments
let horizontalLineSpacing = Math.floor(height/config.lineCount); // Number of pixels to advance in vertical direction
let horizontalLineSpacing = Math.floor(height/parseInt(config.lineCount)); // Number of pixels to advance in vertical direction
//console.log(horizontalLineSpacing);
// Iterate line by line (top line to bottom line) in increments of horizontalLineSpacing
@ -305,9 +323,7 @@
return squiggleData;
}, [{
config: {
...this.settings
},
config: Object.assign({}, this.settings),
image: this.canvasData
}])
.then(result => {
@ -351,24 +367,6 @@
this.webcam.camera = deviceId;
//console.log("On Camera Change Event", deviceId);
},
change(action) {
const editor = this.$refs.editor;
switch (action) {
case 'crop':
editor.crop();
break;
case 'clear':
editor.clear();
break;
case 'restore':
editor.restore();
break;
case 'remove':
editor.reset();
break;
default:
}
},
onInputSelected(type) {
this.inputType = type;
}

Wyświetl plik

@ -1,388 +0,0 @@
<template>
<div class="editor">
<div class="canvas" @dblclick="dblclick">
<img ref="image" :alt="data.name" :src="data.url" @load="start">
</div>
<div class="toolbar" v-if="cropper" @click="click">
<button class="toolbar__button" data-action="move" title="Move (M)"><span class="fa fa-arrows"></span></button>
<button class="toolbar__button" data-action="crop" title="Crop (C)"><span class="fa fa-crop"></span></button>
<button class="toolbar__button" data-action="zoom-in" title="Zoom In (I)"><span class="fa fa-search-plus"></span></button>
<button class="toolbar__button" data-action="zoom-out" title="Zoom Out (O)"><span class="fa fa-search-minus"></span></button>
<button class="toolbar__button" data-action="rotate-left" title="Rotate Left (L)"><span class="fa fa-rotate-left"></span></button>
<button class="toolbar__button" data-action="rotate-right" title="Rotate Right (R)"><span class="fa fa-rotate-right"></span></button>
<button class="toolbar__button" data-action="flip-horizontal" title="Flip Horizontal (H)"><span class="fa fa-arrows-h"></span></button>
<button class="toolbar__button" data-action="flip-vertical" title="Flip Vertical (V)"><span class="fa fa-arrows-v"></span></button>
</div>
</div>
</template>
<script>
import Cropper from 'cropperjs';
export default {
props: {
data: {
type: Object,
default: () => ({}),
},
},
data() {
return {
canvasData: null,
cropBoxData: null,
croppedData: null,
croppedImageCanvas: null,
cropper: null,
};
},
methods: {
click({ target }) {
const { cropper } = this;
const action = target.getAttribute('data-action') || target.parentElement.getAttribute('data-action');
switch (action) {
case 'move':
case 'crop':
cropper.setDragMode(action);
break;
case 'zoom-in':
cropper.zoom(0.1);
break;
case 'zoom-out':
cropper.zoom(-0.1);
break;
case 'rotate-left':
cropper.rotate(-90);
break;
case 'rotate-right':
cropper.rotate(90);
break;
case 'flip-horizontal':
cropper.scaleX(-cropper.getData().scaleX || -1);
break;
case 'flip-vertical':
cropper.scaleY(-cropper.getData().scaleY || -1);
break;
default:
}
},
keydown(e) {
switch (e.key) {
// Undo crop
case 'z':
if (e.ctrlKey) {
e.preventDefault();
this.restore();
}
break;
// Delete the image
case 'Delete':
this.reset();
break;
default:
}
const { cropper } = this;
if (!cropper) {
return;
}
switch (e.key) {
// Crop the image
case 'Enter':
this.crop();
break;
// Clear crop area
case 'Escape':
this.clear();
break;
// Move to the left
case 'ArrowLeft':
e.preventDefault();
cropper.move(-1, 0);
break;
// Move to the top
case 'ArrowUp':
e.preventDefault();
cropper.move(0, -1);
break;
// Move to the right
case 'ArrowRight':
e.preventDefault();
cropper.move(1, 0);
break;
// Move to the bottom
case 'ArrowDown':
e.preventDefault();
cropper.move(0, 1);
break;
// Enter crop mode
case 'c':
cropper.setDragMode('crop');
break;
// Enter move mode
case 'm':
cropper.setDragMode('move');
break;
// Zoom in
case 'i':
cropper.zoom(0.1);
break;
// Zoom out
case 'o':
cropper.zoom(-0.1);
break;
// Rotate left
case 'l':
cropper.rotate(-90);
break;
// Rotate right
case 'r':
cropper.rotate(90);
break;
// Flip horizontal
case 'h':
cropper.scaleX(-cropper.getData().scaleX || -1);
break;
// Flip vertical
case 'v':
cropper.scaleY(-cropper.getData().scaleY || -1);
break;
default:
}
},
dblclick(e) {
if (e.target.className.indexOf('cropper-face') >= 0) {
e.preventDefault();
e.stopPropagation();
this.crop();
}
},
start() {
const { data } = this;
if (data.cropped) {
return;
}
this.cropper = new Cropper(this.$refs.image, {
autoCrop: false,
dragMode: 'move',
background: false,
aspectRatio: 1,
viewMode: 3,
movable: false,
data: {
width: 500,
height: 500
},
ready: () => {
if (this.croppedData) {
this.cropper
.crop()
.setData(this.croppedData)
.setCanvasData(this.canvasData)
.setCropBoxData(this.cropBoxData);
this.croppedData = null;
this.canvasData = null;
this.cropBoxData = null;
}
this.cropper.setDragMode('crop');
},
crop: ({ detail }) => {
const width = detail.width;
const height = detail.height;
if (width < 500 || height < 500 && data.cropping) {
this.cropper.setData({
width: 500,
height: 500,
});
}
if (width > 0 && height > 0 && !data.cropping) {
this.update({
cropping: true,
});
}
},
});
},
stop() {
if (this.cropper) {
this.cropper.destroy();
this.cropper = null;
//this.croppedImageCanvas = null;
//this.
}
},
crop() {
const { cropper, data } = this;
if (data.cropping) {
this.croppedData = cropper.getData();
this.canvasData = cropper.getCanvasData();
this.cropBoxData = cropper.getCropBoxData();
const croppedCanvas = cropper.getCroppedCanvas(data.type === 'image/png' ? {imageSmoothingQuality: "high", width: 500,
height: 500,
minWidth: 500,
minHeight: 500,
maxWidth: 4096,
maxHeight: 4096 } : {
imageSmoothingQuality: "high",
fillColor: '#fff', width: 500,
height: 500,
minWidth: 500,
minHeight: 500,
maxWidth: 4096,
maxHeight: 4096
});
this.update({
cropped: true,
cropping: false,
previousUrl: data.url,
croppedImageData: croppedCanvas,
url: croppedCanvas.toDataURL(data.type),
});
this.stop();
}
},
clear() {
if (this.data.cropping) {
this.cropper.clear();
this.update({
cropping: false,
});
}
},
restore() {
if (this.data.cropped) {
this.update({
cropped: false,
previousUrl: '',
url: this.data.previousUrl,
});
}
},
reset() {
this.stop();
this.update({
cropped: false,
cropping: false,
loaded: false,
name: '',
previousUrl: '',
type: '',
url: '',
croppedImageData: ''
});
},
update(data) {
Object.assign(this.data, data);
},
},
mounted() {
window.addEventListener('keydown', (this.onKeydown = this.keydown.bind(this)));
},
beforeDestroy() {
window.removeEventListener('keydown', this.onKeydown);
this.stop();
},
};
</script>
<style scoped lang="scss">
.editor {
height: 100%;
}
.canvas {
align-items: center;
display: flex;
height: 100%;
justify-content: center;
& > img {
max-height: 100%;
max-width: 100%;
}
}
.toolbar {
background-color: rgba(0, 0, 0, .5);
bottom: 1rem;
color: #fff;
height: 2rem;
left: 50%;
margin-left: -8rem;
position: absolute;
width: 16rem;
z-index: 2015;
}
.toolbar__button {
background-color: transparent;
border-width: 0;
color: #fff;
cursor: pointer;
display: block;
float: left;
font-size: .875rem;
height: 2rem;
text-align: center;
width: 2rem;
&:focus {
outline: none;
}
&:hover {
background-color: #0074d9;
color: #fff;
}
}
</style>

Wyświetl plik

@ -1,106 +0,0 @@
<template>
<div class="loader" @change="change" @dragover="dragover" @drop="drop">
<p>Drop image here or
<label class="browse">browse...
<input class="sr-only" id="file" type="file" accept="image/*">
</label>
</p>
</div>
</template>
<script>
const URL = window.URL || window.webkitURL;
export default {
props: {
data: {
type: Object,
default: () => ({}),
},
},
methods: {
read(files) {
return new Promise((resolve, reject) => {
if (!files || files.length === 0) {
resolve();
return;
}
const file = files[0];
if (/^image\/\w+$/.test(file.type)) {
if (URL) {
resolve({
loaded: true,
name: file.name,
type: file.type,
url: URL.createObjectURL(file),
});
} else {
reject(new Error('Your browser is not supported.'));
}
} else {
reject(new Error('Please choose an image file.'));
}
});
},
change({ target }) {
this.read(target.files).then((data) => {
target.value = '';
this.update(data);
}).catch((e) => {
target.value = '';
this.alert(e);
});
},
dragover(e) {
e.preventDefault();
},
drop(e) {
e.preventDefault();
this.read(e.dataTransfer.files).catch(this.alert);
},
alert(e) {
window.alert(e && e.message ? e.message : e);
},
update(data) {
Object.assign(this.data, data);
},
},
};
</script>
<style scoped lang="scss">
.loader {
border: 2px #666666 dashed;
height: 200px;
display: flex;
align-items: center;
justify-content: center;
.browse {
color: #0074d9;
cursor: pointer;
margin-left: .25rem;
}
.sr-only {
border: 0;
clip: rect(0, 0, 0, 0);
height: 1px;
overflow: hidden;
padding: 0;
position: absolute;
white-space: nowrap;
width: 1px;
z-index: -1;
}
}
</style>

Wyświetl plik

@ -1,76 +0,0 @@
<template>
<div class="navbar">
<nav class="nav" @click="click">
<button type="button" class="nav__button" data-action="restore" title="Undo (Ctrl + Z)" v-if="data.cropped"><span class="fa fa-undo"></span></button>
<button type="button" class="nav__button nav__button--danger" data-action="remove" title="Delete (Delete)" v-if="data.loaded && !data.cropping"><span class="fa fa-trash"></span></button>
<button type="button" class="nav__button nav__button--danger" data-action="clear" title="Cancel (Esc)" v-if="data.cropping"><span class="fa fa-ban"></span></button>
<button type="button" class="nav__button nav__button--success" data-action="crop" title="OK (Enter)" v-if="data.cropping"><span class="fa fa-check"></span></button>
</nav>
</div>
</template>
<script>
export default {
data() {
return {
downloadable: typeof document.createElement('a').download !== 'undefined',
};
},
props: {
data: {
type: Object,
default: () => ({}),
},
},
methods: {
click({ target }) {
const action = target.getAttribute('data-action') || target.parentElement.getAttribute('data-action');
if (action) {
this.$emit('change', action);
}
},
},
};
</script>
<style scoped lang="scss">
.navbar {
height: 200px;
display: flex;
align-items: center;
justify-content: center;
}
.nav__button {
background-color: transparent;
border-width: 0;
color: #fff;
cursor: pointer;
display: block;
float: left;
height: 3rem;
line-height: 3rem;
text-align: center;
width: 3rem;
&:focus {
outline: none;
}
&:hover {
background-color: #0074d9;
color: #fff;
}
}
.nav--success:hover {
background-color: #2ecc40;
}
.nav--danger:hover {
background-color: #ff4136;
}
</style>

Wyświetl plik

@ -137,8 +137,8 @@
}
// Emit video start/live event
this.$refs.video.onloadedmetadata = () => {
this.width = 500;
this.height = 500;
// this.width = 500;
// this.height = 500;
this.$emit("video-live", stream);
};
@ -191,6 +191,7 @@
* load the Camera passed as index!
*/
loadCamera(device) {
console.log('loading camera, width', this.width);
navigator.mediaDevices
.getUserMedia({
video: {

Wyświetl plik

@ -1,5 +1,5 @@
<template>
<svg :view-box.camel="viewbox" :width="width" :height="height">
<svg xmlns="http://www.w3.org/2000/svg" :view-box.camel="viewbox" :width="width" :height="height">
<g>
<svg-chart-line :d="line" :o="options" v-for="(line, index) in lines" :key="index"></svg-chart-line>
</g>
@ -21,11 +21,3 @@
}
}
</script>
<style scoped>
path {
fill: none;
stroke: #0f0f0f;
stroke-width: 1.5px;
}
</style>

Wyświetl plik

@ -1,5 +1,5 @@
<template>
<path :d="pathD"></path>
<path :d="pathD" style="stroke-width: 1.5px; fill: none; stroke: #000000;"></path>
</template>
<script>

Wyświetl plik

@ -86,6 +86,11 @@ html [type="button"] {
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
}
.btn-block {
display: block;
width: 100%;
}
.btn-circle {
width: 36px;
height: 36px;