Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: canvas pen #57

Merged
merged 1 commit into from
Jan 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/BlockHub/BlockHub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { type Block } from './Block/Block'
import TextBox from './TextBoxBlock/TextBox.vue'
import Table from './TableBlock/Table.vue'
import Picture from './PictureBlock/Picture.vue'
import Canvas from './CanvasBlock/Canvas.vue'

class BlockHub {
private _blockMap = Object.create(null)
Expand All @@ -21,6 +22,7 @@ export const BlockViews: { [key: string]: Component } = {
TextBox,
Table,
Picture,
Canvas,
}

export const blockHub = new BlockHub()
65 changes: 65 additions & 0 deletions src/BlockHub/CanvasBlock/Canvas.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<script setup lang="ts">
import { BasicPropName } from '@BlockHub/Block/Block'
import { ref } from 'vue'
import { CanvasBlock } from './CanvasBlock'
import { OriginMap } from '@Kernel/Store/_Store'
import { onMounted } from 'vue'

const { block } = defineProps<{
block: CanvasBlock
}>()

const { x, y, width, height, rotate } = block
const props = ref({ x, y, width, height, rotate })
block.props.events.update.on(({ key, to }) => {
if (['x', 'y', 'width', 'height', 'rotate'].includes(key)) {
const name = key as BasicPropName
props.value[name] = to as number
}
render()
})
onMounted(() => {
render()
})

const canvasRef = ref<HTMLCanvasElement | null>(null)
const render = () => {
const context = (canvasRef.value as HTMLCanvasElement).getContext('2d') as CanvasRenderingContext2D
const { x, y, width, height } = props.value
context.clearRect(x, y, width, height)

const points = block.points
if (points.length === 0) {
return
}
context.save()
context.strokeStyle = 'black'
context.lineWidth = 1
context.beginPath()
const start = points.get(0) as OriginMap
context.moveTo(start.x as number, start.y as number)
for (const point of points.slice(1)) {
const p = point as OriginMap
context.lineTo(p.x as number, p.y as number)
}
context.stroke()
context.restore()
}
</script>

<template>
<canvas
ref="canvasRef"
class="absolute"
:width="props.width"
:height="props.height"
:style="{
left: `${props.x}px`,
top: `${props.y}px`,
width: `${props.width}px`,
height: `${props.height}px`,
rotate: `${props.rotate}deg`,
}"
>
</canvas>
</template>
23 changes: 23 additions & 0 deletions src/BlockHub/CanvasBlock/CanvasBlock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { ArrayStore } from '@Kernel/Store/ArrayStore'
import { Block } from '../Block/Block'

export class CanvasBlock extends Block {
constructor(x: number, y: number, width: number, height: number) {
super('Canvas', x, y, width, height)
this.props.set('points', new ArrayStore())

const points = this.props.get('points') as ArrayStore
points.events.update.on(() => {
// TODO: fix from
this.props.events.update.emit({ key: 'points', from: this.points, to: this.points })
})
}

get points() {
return this.props.get('points') as ArrayStore
}

set points(points: ArrayStore) {
this.props.set('points', points)
}
}
8 changes: 8 additions & 0 deletions src/Kernel/Store/ArrayStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,18 @@ export class ArrayStore {
}>(),
}

get length() {
return this._store.length
}

get(index: number) {
return this._store[index]
}

slice(index: number) {
return this._store.slice(index)
}

insert(index: number, ...values: Array<FullType>) {
const from = [] as Array<FullType>
const to = values
Expand Down
4 changes: 4 additions & 0 deletions src/Kernel/ToolBox/ToolBox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,8 @@ export class ToolBox {
_addListener()
})
}

get currentToolType() {
return this._currentToolType
}
}
57 changes: 57 additions & 0 deletions src/Kernel/ToolBox/controller/PenToolController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { toSlideCoords } from '@Utils/toSlideCoords'
import { ToolController } from './_ToolController'
import { CanvasBlock } from '@BlockHub/CanvasBlock/CanvasBlock'
import { selectionManager, slideManager, toolBox } from '@Kernel/index'
import { ArrayStore } from '@Kernel/Store/ArrayStore'
import { OriginMap } from '@Kernel/Store/_Store'

export class PenToolController extends ToolController {
private _drawing = false
private _canvasBlock?: CanvasBlock
private _range = { left: Infinity, top: Infinity, right: -Infinity, bottom: -Infinity }

handleClick() {}

handleMouseDown(event: MouseEvent): void {
this._drawing = true
const slideElement = event.currentTarget as HTMLElement
const slideRect = slideElement.getBoundingClientRect()
this._canvasBlock = new CanvasBlock(0, 0, slideRect.width, slideRect.height)
slideManager.currentSlide.addBlock(this._canvasBlock)
}

handleMouseMove(event: MouseEvent) {
if (this._drawing) {
const { x, y } = toSlideCoords(event.currentTarget as HTMLElement, event.clientX, event.clientY)
this._range.left = Math.min(this._range.left, x)
this._range.right = Math.max(this._range.right, x)
this._range.top = Math.min(this._range.top, y)
this._range.bottom = Math.max(this._range.bottom, y)
this._canvasBlock?.points.push({ x, y })
}
}

handleMouseUp() {
if (this._drawing) {
this._drawing = false
const block = new CanvasBlock(
this._range.left,
this._range.top,
this._range.right - this._range.left,
this._range.bottom - this._range.top
)
for (const point of this._canvasBlock?.points as ArrayStore) {
const { x, y } = point as OriginMap
block.points.push({
x: (x as number) - this._range.left,
y: (y as number) - this._range.top,
})
}
const slide = slideManager.currentSlide
slide.removeBlock(this._canvasBlock as CanvasBlock)
slide.addBlock(block)
selectionManager.focus(block)
}
toolBox.events.toolChange.emit('Default')
}
}
3 changes: 2 additions & 1 deletion src/Kernel/ToolBox/controller/TextBoxToolController.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { TextBoxBlock } from '@BlockHub/TextBoxBlock/TextBoxBlock'
import { ToolController } from './_ToolController'
import { slideManager, toolBox } from '@Kernel/index'
import { selectionManager, slideManager, toolBox } from '@Kernel/index'
import { TEXT_BOX_DEFAULT_HEIGHT, TEXT_BOX_DEFAULT_WIDTH } from '@Const/block'
import { toSlideCoords } from '@Utils/toSlideCoords'

Expand Down Expand Up @@ -49,6 +49,7 @@ export class TextBoxToolController extends ToolController {
}
this._currentBlock = undefined
this._dragging = false
selectionManager.focus(block)
toolBox.events.toolChange.emit('Default')
}
}
2 changes: 2 additions & 0 deletions src/Kernel/ToolBox/controller/_index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { DefaultToolController } from './DefaultToolController'
import { PenToolController } from './PenToolController'
import { PictureToolController } from './PictureToolController'
import { TextBoxToolController } from './TextBoxToolController'
import { ToolController } from './_ToolController'
Expand All @@ -7,4 +8,5 @@ export const controllers: { [key: string]: ToolController } = {
Default: new DefaultToolController(),
Picture: new PictureToolController(),
TextBox: new TextBoxToolController(),
Pen: new PenToolController(),
}
6 changes: 5 additions & 1 deletion src/Lang/Locale/en-US/ToolBar.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,11 @@
}
},
"draw": {
"text": "Draw"
"text": "Draw",
"tools": {
"title": "Tools",
"pen": "Pen"
}
},
"design": {
"text": "Design"
Expand Down
6 changes: 5 additions & 1 deletion src/Lang/Locale/zh-CN/ToolBar.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,11 @@
}
},
"draw": {
"text": "绘图"
"text": "绘图",
"tools": {
"title": "工具",
"pen": "画笔"
}
},
"design": {
"text": "设计"
Expand Down
5 changes: 4 additions & 1 deletion src/UserInterface/Slide/SlideContainer.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { ref } from 'vue'
import { slideManager, selectionManager } from '@Kernel/index'
import { slideManager, selectionManager, toolBox } from '@Kernel/index'
import Slide from './Slide.vue'
import { onMounted } from 'vue'

Expand All @@ -18,6 +18,9 @@ const dragAreaRect = ref({ x: 0, y: 0, width: 0, height: 0 })
const startCoords = { x: 0, y: 0 }

const handleMouseDown = (event: MouseEvent) => {
if (toolBox.currentToolType !== 'Default') {
return
}
showDragArea.value = true
const { clientX, clientY } = event
startCoords.x = clientX
Expand Down
2 changes: 2 additions & 0 deletions src/UserInterface/ToolBar/ToolBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import ToolTabs from './components/ToolTabs.vue'
import TabContent from './components/TabContent.vue'
import Home from './components/Home/index.vue'
import Insert from './components/Insert/Index.vue'
import Draw from './components/Draw/index.vue'
import SlideShow from './components/SlideShow/index.vue'
import { TOOL_TABS } from './const'

Expand All @@ -15,6 +16,7 @@ const handleClickTab = (tab: string) => {
const components: { [key: string]: Component } = {
Home,
Insert,
Draw,
SlideShow,
}
</script>
Expand Down
14 changes: 14 additions & 0 deletions src/UserInterface/ToolBar/components/Draw/Tools.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<script setup lang="ts">
import { Pencil } from '@icon-park/vue-next'
import MenuWrapper from '../MenuWrapper.vue'
import { toolBox } from '@Kernel/index'
</script>

<template>
<MenuWrapper :name="$t('ToolBar.draw.tools.title')">
<button class="menu-btn flex flex-col items-center" @click="toolBox.events.toolChange.emit('Pen')">
<Pencil theme="outline" size="32" fill="#333" :strokeWidth="2" />
<span class="text-xs mt-1">{{ $t('ToolBar.draw.tools.pen') }}</span>
</button>
</MenuWrapper>
</template>
7 changes: 7 additions & 0 deletions src/UserInterface/ToolBar/components/Draw/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<script setup lang="ts">
import Tools from './Tools.vue'
</script>

<template>
<Tools />
</template>