add basic drawing example
This commit is contained in:
150
docs/src/demos/Examples/Drawing/Component.vue
Normal file
150
docs/src/demos/Examples/Drawing/Component.vue
Normal 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>
|
||||
35
docs/src/demos/Examples/Drawing/Paper.js
Normal file
35
docs/src/demos/Examples/Drawing/Paper.js
Normal 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)
|
||||
},
|
||||
})
|
||||
57
docs/src/demos/Examples/Drawing/index.vue
Normal file
57
docs/src/demos/Examples/Drawing/index.vue
Normal 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>
|
||||
Reference in New Issue
Block a user