diff --git a/.DS_Store b/.DS_Store index 94e91fc9c7816fc97b471daafcb884f5f9e977d1..2cf9dd3fbe680113a2c17d01e4ec4c06c14fd222 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/CHANGELOG.md b/CHANGELOG.md index c12bdeb4cd01553f5b4ed32ed113370402359bf4..8b5737b6ba6f9f2682a4aa0e97f3b4b4c9e26acb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +# 0.1.17 + +_25th June 2020_ + +### Added + +- Local _scribble_ mode, selecting the pencil mode on touch devices allows drawing, this is not saved and it local to you only (for now). + # 0.1.16 _22nd June 2020_ diff --git a/app/package.json b/app/package.json index 0fde8942fdddd168d1e8b956e5271a59c8744dfd..202e7335e1ba44af10e65f3a240251e9610849fa 100644 --- a/app/package.json +++ b/app/package.json @@ -1,6 +1,6 @@ { "name": "nodenogg.in", - "version": "0.1.16", + "version": "0.1.17", "private": true, "scripts": { "serve": "vue-cli-service serve", diff --git a/app/src/components/NodesLayer.vue b/app/src/components/NodesLayer.vue index fd84448926d29a8ba04b306cbb45899524c1d37f..df45f628aa6838b65b57dfa1e3c0432b395490c9 100644 --- a/app/src/components/NodesLayer.vue +++ b/app/src/components/NodesLayer.vue @@ -308,6 +308,7 @@ export default { <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> .node { + position: relative; } diff --git a/app/src/components/ScribbleLayer.vue b/app/src/components/ScribbleLayer.vue new file mode 100644 index 0000000000000000000000000000000000000000..27e20385f20409a77ec06f2acb2c52d3d5f7cf1e --- /dev/null +++ b/app/src/components/ScribbleLayer.vue @@ -0,0 +1,195 @@ +<template> + <div class="scribble"> + <canvas id="canvas"></canvas> + </div> +</template> + +<script> +//FIXME: touch / mouse is not under cursor +var ongoingTouches = [] + +export default { + name: 'ScribbleLayer', + + props: { + drawready: Boolean, + }, + + data() { + return { + painting: this.drawready, + canvas: null, + ctx: null, + } + }, + + methods: { + // startPainting(e) { + // this.painting = this.drawready + // if (this.painting == true) { + // // console.log(this.painting) + // this.draw(e) + // } + // }, + // finishedPainting() { + // this.painting = false + // // console.log(this.painting) + // this.ctx.beginPath() + // }, + // draw(e) { + // if (!this.painting) return + + // this.ctx.lineWidth = 6 + // this.ctx.lineCap = 'round' + + // this.ctx.lineTo(e.clientX, e.clientY) + // this.ctx.stroke() + + // this.ctx.beginPath() + // this.ctx.moveTo(e.clientX, e.clientY) + // }, + + // touch methods + + handleStart(evt) { + this.painting = this.drawready + // console.log(this.painting) + if (this.painting == true) { + evt.preventDefault() + console.log('touchstart.') + var el = document.getElementById('canvas') + var ctx = el.getContext('2d') + var touches = evt.changedTouches + + for (var i = 0; i < touches.length; i++) { + //console.log('touchstart:' + i + '...') + ongoingTouches.push(this.copyTouch(touches[i])) + var color = this.colorForTouch(touches[i]) + ctx.beginPath() + ctx.arc(touches[i].pageX, touches[i].pageY, 4, 0, 2 * Math.PI, false) // a circle at the start + ctx.fillStyle = color + ctx.fill() + // console.log(touches[i].pageY) + } + } + }, + + handleMove(evt) { + evt.preventDefault() + var el = document.getElementById('canvas') + var ctx = el.getContext('2d') + var touches = evt.changedTouches + if (this.painting == true) { + for (var i = 0; i < touches.length; i++) { + var color = this.colorForTouch(touches[i]) + var idx = this.ongoingTouchIndexById(touches[i].identifier) + + if (idx >= 0) { + // console.log('continuing touch ' + idx) + ctx.beginPath() + // console.log( + // 'ctx.moveTo(' + + // ongoingTouches[idx].pageX + + // ', ' + + // ongoingTouches[idx].pageY + + // ');' + // ) + ctx.moveTo(ongoingTouches[idx].pageX, ongoingTouches[idx].pageY) + // console.log( + // 'ctx.lineTo(' + touches[i].pageX + ', ' + touches[i].pageY + ');' + // ) + ctx.lineTo(touches[i].pageX, touches[i].pageY) + ctx.lineWidth = 4 + ctx.strokeStyle = color + ctx.stroke() + + ongoingTouches.splice(idx, 1, this.copyTouch(touches[i])) // swap in the new touch record + // console.log('.') + } else { + // console.log("can't figure out which touch to continue") + } + } + } + }, + handleEnd(evt) { + evt.preventDefault() + // log('touchend') + var el = document.getElementById('canvas') + var ctx = el.getContext('2d') + var touches = evt.changedTouches + + for (var i = 0; i < touches.length; i++) { + var color = this.colorForTouch(touches[i]) + var idx = this.ongoingTouchIndexById(touches[i].identifier) + + if (idx >= 0) { + ctx.lineWidth = 4 + ctx.fillStyle = color + ctx.beginPath() + ctx.moveTo(ongoingTouches[idx].pageX, ongoingTouches[idx].pageY) + ctx.lineTo(touches[i].pageX, touches[i].pageY) + ctx.fillRect(touches[i].pageX - 4, touches[i].pageY - 4, 8, 8) // and a square at the end + ongoingTouches.splice(idx, 1) // remove it; we're done + } else { + // console.log("can't figure out which touch to end") + } + } + }, + + handleCancel(evt) { + evt.preventDefault() + + // console.log('touchcancel.') + var touches = evt.changedTouches + + for (var i = 0; i < touches.length; i++) { + var idx = this.ongoingTouchIndexById(touches[i].identifier) + ongoingTouches.splice(idx, 1) // remove it; we're done + } + }, + colorForTouch(touch) { + var r = touch.identifier % 16 + var g = Math.floor(touch.identifier / 3) % 16 + var b = Math.floor(touch.identifier / 7) % 16 + r = r.toString(16) // make it a hex digit + g = g.toString(16) // make it a hex digit + b = b.toString(16) // make it a hex digit + var color = '#' + r + g + b + // console.log( + // 'color for touch with identifier ' + touch.identifier + ' = ' + color + // ) + return color + }, + + copyTouch({ identifier, pageX, pageY }) { + return { identifier, pageX, pageY } + }, + ongoingTouchIndexById(idToFind) { + for (var i = 0; i < ongoingTouches.length; i++) { + var id = ongoingTouches[i].identifier + + if (id == idToFind) { + return i + } + } + return -1 // not found + }, + }, + + mounted() { + this.canvas = document.getElementById('canvas') + this.ctx = this.canvas.getContext('2d') + + // Resize canvas + this.canvas.height = window.innerHeight + this.canvas.width = window.innerWidth + + this.canvas.addEventListener('touchstart', this.handleStart, false) + this.canvas.addEventListener('touchend', this.handleEnd, false) + this.canvas.addEventListener('touchcancel', this.handleCancel, false) + this.canvas.addEventListener('touchmove', this.handleMove, false) + }, +} +</script> + +<style lang="css" scoped></style> diff --git a/app/src/experimental/ModeToolbar.vue b/app/src/experimental/ModeToolbar.vue index 17a14621d937cf89e6028029689d7ee097168fe4..51a547253b60171d3b68ec54f074b888ec5b7ac6 100644 --- a/app/src/experimental/ModeToolbar.vue +++ b/app/src/experimental/ModeToolbar.vue @@ -44,15 +44,17 @@ export default { } if (mode == 'upload') { this.$emit('uploadAdded') - // onFileSelected(event) { - // this.selectedFile = event.target.files[0] - // } } if (mode == 'copy') { this.$emit('copyDone') - // onFileSelected(event) { - // this.selectedFile = event.target.files[0] - // } + } + if (mode == 'draw') { + this.$emit('drawOn') + // console.log(mode) + } + if (mode != 'draw') { + this.$emit('drawOff') + //console.log(mode) } }, diff --git a/app/src/experimental/PanZoomContainer.vue b/app/src/experimental/PanZoomContainer.vue index 8b43d1cfe81bceebe6afa979dc497702ac573f3a..2f258e4f792bcd13436ac8ac6463a1d8584e73e9 100644 --- a/app/src/experimental/PanZoomContainer.vue +++ b/app/src/experimental/PanZoomContainer.vue @@ -198,6 +198,8 @@ export default { constrainTranslation(newTranslation, this.translationBounds) ) }, + + // FIXME: I dont think this is working handleMultiTouch(e) { const [newTranslation, newScale] = changeViewFromMultiTouchEvent( this.interaction.origin.points, diff --git a/app/src/views/Home.vue b/app/src/views/Home.vue index 744ed3141791ebd4a9c366cd80352ee009937f82..6f0da4bfc78a7797368114c6a601d8abdbc3aeb0 100644 --- a/app/src/views/Home.vue +++ b/app/src/views/Home.vue @@ -33,6 +33,7 @@ v-bind:height="height" v-bind:connections="connections" /> + <PanZoomContainer v-bind:width="width" v-bind:height="height" @@ -54,6 +55,7 @@ v-bind:nodetext="value.node_text" /> </div> + <div v-else> <OtherNodeslayer v-for="value in otherNodes" @@ -73,6 +75,7 @@ @editTrue="(e) => editTrue(e)" /> </div> + <ScribbleLayer v-bind:drawready="drawready"></ScribbleLayer> </PanZoomContainer> <ModeToolbar @@ -80,6 +83,8 @@ @onlineTriggered="onlineTriggered()" @uploadAdded="uploadAdded()" @copyDone="copyDone()" + @drawOn="drawOn()" + @drawOff="drawOff()" /> <ViewToolbar /> <UploadLayer @@ -98,6 +103,7 @@ import PanZoomContainer from '@/experimental/PanZoomContainer' import ConnectionsLayer from '@/experimental/layers/ConnectionsLayer' import NodesLayer from '@/components/NodesLayer' import OffLine from '@/components/OffLine' +import ScribbleLayer from '@/components/ScribbleLayer' import UploadLayer from '@/components/UploadLayer' import OtherNodeslayer from '@/components/OtherNodeslayer.vue' import OnBoard from '@/components/OnBoard.vue' @@ -121,6 +127,7 @@ export default { offline: false, uploadready: false, copyready: false, + drawready: false, } }, computed: { @@ -184,6 +191,15 @@ export default { this.$store.dispatch('shortcutState', e) }, + drawOn() { + this.drawready = !this.drawready + //console.log(this.drawready) + }, + + drawOff() { + this.drawready = false + }, + // This is here to support the shortcuts addNode() { this.$store.dispatch('addNode') @@ -207,6 +223,7 @@ export default { OnBoard, OffLine, UploadLayer, + ScribbleLayer, }, } </script>