kopia lustrzana https://github.com/msurguy/SquiggleCam
switched to Croppa
rodzic
5e6789fc5c
commit
b9ed1d6196
|
@ -1622,6 +1622,11 @@
|
|||
"integrity": "sha512-ogq4NbUWf1uG/j66k0AmiO3GjqJAlQyF8n4w8a954cbCyFKmYGvRtgz6qkq2fWuduTXHibX7GyYL5Pg58Aks2g==",
|
||||
"dev": true
|
||||
},
|
||||
"canvas-exif-orientation": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/canvas-exif-orientation/-/canvas-exif-orientation-0.4.0.tgz",
|
||||
"integrity": "sha1-tIfzcBmYqeh56xBAELKlgRU2i2s="
|
||||
},
|
||||
"caseless": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
|
||||
|
@ -10974,6 +10979,15 @@
|
|||
"resolved": "https://registry.npmjs.org/vue/-/vue-2.5.21.tgz",
|
||||
"integrity": "sha512-Aejvyyfhn0zjVeLvXd70h4hrE4zZDx1wfZqia6ekkobLmUZ+vNFQer53B4fu0EjWBSiqApxPejzkO1Znt3joxQ=="
|
||||
},
|
||||
"vue-croppa": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/vue-croppa/-/vue-croppa-1.3.8.tgz",
|
||||
"integrity": "sha512-WwYgEKscTCD7BzhnbfRJfzWIU6RcMq2JRimB3aI5gGzpADmpKuqmDh9+oVfiZaEnpmRthgXZxcAvbxU6CeIU9w==",
|
||||
"requires": {
|
||||
"canvas-exif-orientation": "^0.4.0",
|
||||
"object-assign": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"vue-hot-reload-api": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.1.tgz",
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
"normalize.css": "^8.0.1",
|
||||
"pica": "^5.0.0",
|
||||
"vue": "^2.5.2",
|
||||
"vue-croppa": "^1.3.8",
|
||||
"vue-worker": "^1.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
115
src/App.vue
115
src/App.vue
|
@ -11,8 +11,8 @@
|
|||
<image-chooser @selected="onInputSelected"></image-chooser>
|
||||
|
||||
<div class="image-upload" v-if="this.inputType === 'upload'">
|
||||
<loader v-if="!cropper.loaded" ref="loader" :data="cropper"></loader>
|
||||
<crop-actions v-if="cropper.loaded" :data="cropper" @change="change"></crop-actions>
|
||||
<croppa v-model="myCroppa" :width="250" :height="250" :preventWhiteSpace="true" :quality="2"></croppa>
|
||||
<button @click="uploadCroppedImage">Output JPEG</button>
|
||||
</div>
|
||||
|
||||
<div class="image-webcam" v-if="this.inputType === 'webcam'">
|
||||
|
@ -102,24 +102,21 @@
|
|||
</div>
|
||||
<div class="actions">
|
||||
<button class="btn">
|
||||
PNG
|
||||
JPG
|
||||
</button>
|
||||
<button class="btn">
|
||||
<button class="btn" @click="downloadSVG">
|
||||
SVG
|
||||
</button>
|
||||
<button class="btn">
|
||||
ZIP
|
||||
</button>
|
||||
</div>
|
||||
<div class="panel-toggle">
|
||||
<button id="panelToggler" class="btn"><span class="fa fa-chevron-left"></span></button>
|
||||
</div>
|
||||
|
||||
</aside>
|
||||
<main>
|
||||
<div v-if="canvasData" class="svg-container" style="padding: 10px;" ref="container">
|
||||
<svg-chart :lines="lines" :options="line" :width="settings.width" :height="settings.height"></svg-chart>
|
||||
<svg-chart ref="svgResult" :lines="lines" :options="line" :width="settings.width" :height="settings.height"></svg-chart>
|
||||
</div>
|
||||
<editor v-if="cropper.loaded" ref="editor" :data="cropper"></editor>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -127,9 +124,9 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Navbar from './components/Navbar'
|
||||
import Loader from './components/Loader'
|
||||
import Editor from './components/Editor'
|
||||
// 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';
|
||||
|
@ -137,15 +134,17 @@
|
|||
export default {
|
||||
name: 'App',
|
||||
components: {
|
||||
cropActions: Navbar ,
|
||||
loader: Loader,
|
||||
editor: Editor,
|
||||
//cropActions: Navbar ,
|
||||
//loader: Loader,
|
||||
//editor: Editor,
|
||||
imageChooser: ImageChooser,
|
||||
webcam: WebCam,
|
||||
svgChart: svgChart
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
myCroppa: {},
|
||||
dataUrl: '',
|
||||
line: {
|
||||
smoothing: 0.25,
|
||||
flattening: 0.5
|
||||
|
@ -173,16 +172,16 @@
|
|||
devices: [],
|
||||
streaming: false
|
||||
},
|
||||
cropper: {
|
||||
cropped: false,
|
||||
cropping: false,
|
||||
loaded: false,
|
||||
name: '',
|
||||
previousUrl: '',
|
||||
type: '',
|
||||
url: '',
|
||||
croppedImageData: ''
|
||||
},
|
||||
// cropper: {
|
||||
// cropped: false,
|
||||
// cropping: false,
|
||||
// loaded: false,
|
||||
// name: '',
|
||||
// previousUrl: '',
|
||||
// type: '',
|
||||
// url: '',
|
||||
// croppedImageData: ''
|
||||
// },
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -198,12 +197,12 @@
|
|||
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;
|
||||
},
|
||||
// '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,6 +218,28 @@
|
|||
},
|
||||
|
||||
methods: {
|
||||
downloadSVG(){
|
||||
console.log(this.$refs.svgResult.$el.innerHTML);
|
||||
},
|
||||
uploadCroppedImage() {
|
||||
this.myCroppa.generateBlob((blob) => {
|
||||
let canvas = document.createElement("canvas");
|
||||
canvas.width = 500;
|
||||
canvas.height = 500;
|
||||
// const ctx = canvas.getContext("2d");
|
||||
// this.canvasData = ctx.getImageData(0, 0, 500, 500);
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
let img = new Image();
|
||||
|
||||
img.onload = () => {
|
||||
ctx.drawImage(img, 0, 0)
|
||||
this.canvasData = ctx.getImageData(0, 0, 500, 500);
|
||||
};
|
||||
|
||||
img.src = URL.createObjectURL(blob);
|
||||
}, 'image/jpeg', 1)
|
||||
},
|
||||
processImage() {
|
||||
this.$worker.run((data) => {
|
||||
// Gather all necessary data from the main thread
|
||||
|
@ -252,7 +273,7 @@
|
|||
// 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
|
||||
console.log(config.spacing, width);
|
||||
//console.log(config.spacing, width);
|
||||
for (let x = config.spacing; x < width; x += config.spacing ) {
|
||||
|
||||
currentHorizontalPixelIndex = x + currentVerticalPixelIndex; // Get array position of current pixel
|
||||
|
@ -358,21 +379,21 @@
|
|||
<style lang="scss">
|
||||
@import './styles/index.scss';
|
||||
|
||||
.cropper-header {
|
||||
background-color: #666;
|
||||
height: 3rem;
|
||||
overflow: hidden;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.cropper-header {
|
||||
padding-left: 1.5rem;
|
||||
padding-right: 1.5rem;
|
||||
}
|
||||
}
|
||||
/*.cropper-header {*/
|
||||
/*background-color: #666;*/
|
||||
/*height: 3rem;*/
|
||||
/*overflow: hidden;*/
|
||||
/*padding-left: 1rem;*/
|
||||
/*padding-right: 1rem;*/
|
||||
/*position: relative;*/
|
||||
/*z-index: 1;*/
|
||||
/*}*/
|
||||
/*@media (min-width: 768px) {*/
|
||||
/*.cropper-header {*/
|
||||
/*padding-left: 1.5rem;*/
|
||||
/*padding-right: 1.5rem;*/
|
||||
/*}*/
|
||||
/*}*/
|
||||
.title {
|
||||
color: #fff;
|
||||
display: block;
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
<div class="button-group stretch">
|
||||
<label>Input:</label>
|
||||
<button id="upload" :disabled="disabled" v-bind:class="{ active: selectedItem === 'upload' }" class="btn" @click="toggle">File</button>
|
||||
<!--<button id="url" :disabled="disabled" v-bind:class="{ active: selectedItem === 'url' }" class="btn" @click="toggle">URL</button>-->
|
||||
<button id="webcam" :disabled="disabled" class="btn" v-bind:class="{ active: selectedItem === 'webcam' }" @click="toggle">WebCam</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "ImageTransformer"
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -1,13 +0,0 @@
|
|||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "SquiggleDataRetriever"
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -1,13 +0,0 @@
|
|||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "SquiggleRenderer"
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -23,5 +23,9 @@
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
path {
|
||||
fill: none;
|
||||
stroke: #0f0f0f;
|
||||
stroke-width: 1.5px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<path :style="styles.path" :d="pathD"></path>
|
||||
<path :d="pathD"></path>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -9,15 +9,6 @@
|
|||
name: "svgChartLine",
|
||||
props: ["d", "o"],
|
||||
computed: {
|
||||
styles() {
|
||||
return {
|
||||
path: {
|
||||
fill: 'none',
|
||||
stroke: "#000",
|
||||
strokeWidth: 1
|
||||
}
|
||||
};
|
||||
},
|
||||
pathD() {
|
||||
return this.pointsPositions.reduce((acc, e, i, a) => i === 0
|
||||
? `M ${e[0]},${e[1]}`
|
||||
|
@ -45,20 +36,16 @@
|
|||
const flat = lib.map(Math.cos(o.angle) * this.o.flattening, 0, 1, 1, 0)
|
||||
const angle = o.angle * flat + (reverse ? Math.PI : 0);
|
||||
const length = o.length * this.o.smoothing;
|
||||
const x = current[0] + Math.cos(angle) * length;
|
||||
const y = current[1] + Math.sin(angle) * length;
|
||||
const x = Math.round((current[0] + Math.cos(angle) * length) * 100) / 100 ;
|
||||
const y = Math.round((current[1] + Math.sin(angle) * length) * 100) / 100 ;
|
||||
return [x, y];
|
||||
},
|
||||
bezierCommand(point, i, a) {
|
||||
const cps = this.controlPoint(a[i - 1], a[i - 2], point);
|
||||
const cpe = this.controlPoint(point, a[i - 1], a[i + 1], true);
|
||||
//const close = i === a.length - 1 ? " z" : "";
|
||||
return `C ${cps[0]},${cps[1]} ${cpe[0]},${cpe[1]} ${point[0]},${point[1]}`;
|
||||
return `C ${cps[0]},${cps[1]} ${cpe[0]},${cpe[1]} ${Math.round(point[0] * 100) / 100},${Math.round(point[1]*100)/100}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
|
11
src/main.js
11
src/main.js
|
@ -4,13 +4,18 @@ import 'normalize.css'
|
|||
import Vue from 'vue'
|
||||
import App from './App'
|
||||
import VueWorker from 'vue-worker';
|
||||
import Croppa from 'vue-croppa'
|
||||
|
||||
//import './styles/index.scss'
|
||||
import 'cropperjs/dist/cropper.css';
|
||||
//import 'cropperjs/dist/cropper.css';
|
||||
|
||||
Vue.config.productionTip = false
|
||||
import 'vue-croppa/dist/vue-croppa.css'
|
||||
|
||||
|
||||
Vue.config.productionTip = false;
|
||||
|
||||
Vue.use(VueWorker);
|
||||
Vue.use(Croppa);
|
||||
|
||||
|
||||
/* eslint-disable no-new */
|
||||
|
@ -18,4 +23,4 @@ new Vue({
|
|||
el: '#app',
|
||||
components: { App },
|
||||
template: '<App/>'
|
||||
})
|
||||
});
|
||||
|
|
|
@ -344,25 +344,6 @@ input[type="range"]:focus::-ms-fill-upper {
|
|||
align-content: space-between;
|
||||
}
|
||||
|
||||
.panel-toggle {
|
||||
position: absolute;
|
||||
right: -12px;
|
||||
bottom: 50px;
|
||||
z-index: 1;
|
||||
//align-self: center;
|
||||
|
||||
.btn {
|
||||
z-index: 100;
|
||||
background-color: #000;
|
||||
padding: 20px 0;
|
||||
}
|
||||
.btn:hover {
|
||||
color: #fff;
|
||||
background-color: #0069d9;
|
||||
border-color: #0062cc;
|
||||
}
|
||||
}
|
||||
|
||||
.image-webcam {
|
||||
position: relative;
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue