add basic drawing example

This commit is contained in:
Philipp Kühn
2020-12-05 20:56:12 +01:00
parent 29d076c4c4
commit 3157e7f763
7 changed files with 505 additions and 6 deletions

View File

@@ -0,0 +1,150 @@
<template>
<node-view-wrapper class="draw">
<button @click="clear">
clear
</button>
<svg viewBox="0 0 500 250" ref="canvas">
<template v-for="line in node.attrs.lines">
<path
v-if="line.id !== currentId"
:key="line.id"
:d="line.d"
:id="`id-${line.id}`"
/>
</template>
</svg>
</node-view-wrapper>
</template>
<script>
import { v4 as uuid } from 'uuid'
import * as d3 from 'd3'
export default {
props: {
updateAttributes: {
type: Function,
required: true,
},
node: {
type: Object,
required: true,
},
},
data() {
return {
currentId: uuid(),
}
},
methods: {
clear() {
this.updateAttributes({
lines: [],
})
},
},
mounted() {
let ptdata = []
let path
let drawing = false
const line = d3.line()
.x(d => d.x)
.y(d => d.y)
const svg = d3.select(this.$refs.canvas)
const listen = event => {
drawing = true
ptdata = [] // reset point data
path = svg.append('path') // start a new line
.data([ptdata])
.attr('id', `id-${this.currentId}`)
.attr('d', line)
if (event.type === 'mousedown') {
svg.on('mousemove', onmove)
} else {
svg.on('touchmove', onmove)
}
}
const ignore = event => {
svg.on('mousemove', null)
svg.on('touchmove', null)
if (!drawing) {
return
}
drawing = false
d3.select(this.$refs.canvas).select(`#id-${this.currentId}`).remove()
this.currentId = uuid()
}
svg
.on('mousedown', listen)
.on('touchstart', listen)
.on('touchend', ignore)
.on('touchleave', ignore)
.on('mouseup', ignore)
.on('mouseleave', ignore)
// ignore default touch behavior
const touchEvents = ['touchstart', 'touchmove', 'touchend']
touchEvents.forEach(eventName => {
document.body.addEventListener(eventName, e => {
e.preventDefault()
})
})
function onmove(event) {
const point = d3.pointer(event)
// push a new data point onto the back
ptdata.push({ x: point[0], y: point[1] })
tick()
}
const tick = () => {
path.attr('d', points => {
const d = line(points)
const lines = this.node.attrs.lines.filter(item => item.id !== this.currentId)
this.updateAttributes({
lines: [
...lines,
{
id: this.currentId,
d,
},
],
})
return d
})
}
},
}
</script>
<style lang="scss">
.draw {
svg {
background: #EEE;
cursor: crosshair;
}
path {
fill: none;
stroke: #000;
stroke-width: 2;
}
}
</style>

View File

@@ -0,0 +1,35 @@
import { Node, mergeAttributes } from '@tiptap/core'
import { VueRenderer } from '@tiptap/vue'
import Component from './Component.vue'
export default Node.create({
name: 'paper',
group: 'block',
atom: true,
addAttributes() {
return {
lines: {
default: [],
},
}
},
parseHTML() {
return [
{
tag: 'div[data-type="paper"]',
},
]
},
renderHTML({ HTMLAttributes }) {
return ['div', mergeAttributes(HTMLAttributes, { 'data-type': 'paper' })]
},
addNodeView() {
return VueRenderer(Component)
},
})

View File

@@ -0,0 +1,57 @@
<template>
<div v-if="editor">
<editor-content :editor="editor" />
</div>
</template>
<script>
import Collaboration from '@tiptap/extension-collaboration'
import { Editor, EditorContent } from '@tiptap/vue-starter-kit'
import Document from '@tiptap/extension-document'
import Paragraph from '@tiptap/extension-paragraph'
import Text from '@tiptap/extension-text'
import * as Y from 'yjs'
import { WebsocketProvider } from 'y-websocket'
import Paper from './Paper.js'
export default {
components: {
EditorContent,
},
data() {
return {
editor: null,
}
},
mounted() {
const ydoc = new Y.Doc()
const provider = new WebsocketProvider('wss://websocket.tiptap.dev', 'tiptap-draw-example', ydoc)
this.editor = new Editor({
extensions: [
Document.extend({
content: 'paper paragraph',
}),
Paragraph,
Text,
Collaboration.configure({
provider,
}),
Paper,
],
content: `
<div data-type="paper"></div>
`,
onUpdate: () => {
// console.log(this.editor.getJSON())
},
})
},
beforeDestroy() {
this.editor.destroy()
},
}
</script>