Skip to content

Commit

Permalink
Merge pull request #40 from LHRUN/dev
Browse files Browse the repository at this point in the history
add canvas background image
  • Loading branch information
LHRUN authored Jun 10, 2024
2 parents cbbfe02 + 89a91c2 commit 3cbba2c
Show file tree
Hide file tree
Showing 18 changed files with 264 additions and 34 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# 1.4.0

- add canvas background image

# 1.3.1

### Feat
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Link: [https://songlh.top/paint-board/](https://songlh.top/paint-board/)
- Layer settings are supported for all drawings, including Move Layer Up, Move Layer Down, Move to Top, and Move to Bottom.
- All drawings support transparency configurations.
+ Drawing Board Configuration
- Drawing board support for configuring background color and transparency configurations.
- The drawing board supports background configuration, including colour, background image, and transparency.
- The drawing board supports customized width and height configurations.
- Supports painting caching, enabling caching will improve painting performance in the presence of large amounts of painted content, while disabling caching will improve canvas sharpness.
- Added Guide Line drawing feature.
Expand Down
2 changes: 1 addition & 1 deletion README.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Link: [https://songlh.top/paint-board/](https://songlh.top/paint-board/)
- 所有绘制内容均支持图层设置,包括向上移动层级、向下移动层级、移动至顶层和移动至底层。
- 所有绘制内容支持透明度配置。
+ 画板配置
- 画板支持配置背景颜色和透明度配置
- 画板支持配置背景配置, 包括颜色, 背景图, 透明度
- 画板支持自定义宽高配置。
- 支持绘画缓存,在存在大量绘制内容的情况下,启用缓存将提高绘制性能,而禁用缓存则会提升画布清晰度。
- 新增辅助线绘制功能。
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "paint-board",
"private": true,
"version": "1.3.1",
"version": "1.4.0",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
2 changes: 1 addition & 1 deletion src/components/guideInfo/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const GuideInfo: React.FC = () => {
className="ml-5 cursor-pointer border-solid border-4 border-[#7b8fa1] rounded-full hover:border-[#567189] flex items-center justify-center"
onClick={handleChangLang}
>
{language === 'en' ? <EnIcon /> : <ZhIcon />}
{language === 'en' ? <ZhIcon /> : <EnIcon />}
</span>
</div>
<GuideInfoSwiper />
Expand Down
8 changes: 8 additions & 0 deletions src/components/icons/clear.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions src/components/icons/uploadSuccess.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
74 changes: 73 additions & 1 deletion src/components/toolPanel/boardConfig/backgroundConfig/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback, useEffect } from 'react'
import { useCallback, useEffect, ChangeEvent, MouseEvent } from 'react'
import { useTranslation } from 'react-i18next'
import useBoardStore from '@/store/board'

Expand All @@ -7,13 +7,21 @@ import { debounce } from 'lodash'
import { rgbaToHex } from '@/utils/common/color'

import OpacityIcon from '@/components/icons/opacity.svg?react'
import UploadIcon from '@/components/icons/boardOperation/upload.svg?react'
import ClearIcon from '@/components/icons/clear.svg?react'
import UploadSuccessIcon from '@/components/icons/uploadSuccess.svg?react'

const BackgroundConfig = () => {
const {
backgroundColor,
backgroundOpacity,
hasBackgroundImage,
backgroundImageOpacity,
updateBackgroundColor,
updateBackgroundOpacity,
updateBackgroundImage,
updateBackgroundImageOpacity,
cleanBackgroundImage,
initBackground
} = useBoardStore()
const { t } = useTranslation()
Expand All @@ -33,6 +41,32 @@ const BackgroundConfig = () => {
}
}, [initBackground])

// upload background image file
const uploadImage = (e: ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0]
if (!file) {
return
}

const reader = new FileReader()
reader.onload = (fEvent) => {
const data = fEvent.target?.result
if (data) {
if (data && typeof data === 'string') {
updateBackgroundImage(data)
}
}
e.target.value = ''
}
reader.readAsDataURL(file)
}

const clickCleanBackgroundImage = (e: MouseEvent) => {
e.preventDefault()
e.stopPropagation()
cleanBackgroundImage()
}

return (
<div className="form-control mt-3">
<div className="font-bold font-fredokaOne text-sm">
Expand Down Expand Up @@ -65,6 +99,44 @@ const BackgroundConfig = () => {
}}
/>
</div>
<div className="mt-3 flex items-center w-full">
<label
htmlFor="image-upload"
className="shrink-0 cursor-pointer rounded relative hover:bg-slate-200"
>
{hasBackgroundImage ? (
<>
<ClearIcon
onClick={clickCleanBackgroundImage}
className="absolute top-[-6px] right-[-6px] rounded-full w-3 h-3 cursor-pointer"
/>
<UploadSuccessIcon className="w-7 h-7 object-contain" />
</>
) : (
<UploadIcon />
)}
</label>
<input
type="file"
id="image-upload"
className="hidden"
onChange={uploadImage}
/>
<div className="divider divider-horizontal mx-3"></div>
<OpacityIcon className="mr-2" />
<input
className="range range-primary range-xs"
type="range"
min="0"
max="1"
step="0.01"
value={String(backgroundImageOpacity)}
onChange={(e) => {
updateBackgroundImageOpacity(Number(e.target.value))
saveHistory()
}}
/>
</div>
</div>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import AddColorIcon from '@/components/icons/addColor.svg?react'
import useDrawStore from '@/store/draw'
import { useTranslation } from 'react-i18next'

import ClearIcon from '@/components/icons/clear.svg?react'

const DrawColorConfig = () => {
const { drawColors, updateDrawColors } = useDrawStore()
const { t } = useTranslation()
Expand All @@ -28,7 +30,7 @@ const DrawColorConfig = () => {
<div className="mt-2 flex items-center w-full">
{drawColors.map((color, i) => {
return (
<div className="w-7 h-7 mr-2 indicator" key={i}>
<div className="w-7 h-7 mr-2 relative" key={i}>
<input
type="color"
value={color}
Expand All @@ -38,13 +40,10 @@ const DrawColorConfig = () => {
className="colorInput"
/>
{drawColors.length > 1 && (
<span
<ClearIcon
onClick={() => deleteDrawColor(i)}
className="indicator-item badge badge-secondary w-3 h-3 p-0 text-sm bg-black text-white border-black cursor-pointer block text-center"
style={{ lineHeight: '0.5rem' }}
>
x
</span>
className="absolute top-[-6px] right-[-6px] rounded-full w-3 h-3 cursor-pointer"
/>
)}
</div>
)
Expand Down
2 changes: 1 addition & 1 deletion src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
"line6": "All drawings support transparency configurations"
},
"BoardMode": {
"line1": "Drawing board support for configuring background color and transparency configurations",
"line1": "The drawing board supports background configuration, including colour, background image, and transparency",
"line2": "The drawing board supports customized width and height configurations",
"line3": "Support for drawing cache enable. Enabling caching will improve drawing performance in the presence of large amounts of drawing content, while disabling caching will improve canvas clarity.",
"line4": "Supports guide line on and off"
Expand Down
2 changes: 1 addition & 1 deletion src/i18n/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
"line6": "所有绘制内容支持透明度配置"
},
"BoardMode": {
"line1": "画板支持配置背景颜色和透明度配置",
"line1": "画板支持配置背景配置, 包括颜色, 背景图, 透明度",
"line2": "画板支持自定义宽高配置",
"line3": "支持绘制缓存开启. 在存在大量绘制内容的情况下,启用缓存将提高绘制性能,而禁用缓存则会提升画布清晰度",
"line4": "支持辅助线的开启与关闭"
Expand Down
67 changes: 66 additions & 1 deletion src/store/board.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ import { paintBoard } from '@/utils/paintBoard'
import { create } from 'zustand'
import { persist } from 'zustand/middleware'
import { alignGuideLine } from '@/utils/common/fabricMixin/alignGuideLine'
import {
updateCanvasBackgroundImage,
handleBackgroundImageWhenCanvasSizeChange
} from '@/utils/common/background'

interface BoardState {
mode: string // operating mode
Expand All @@ -19,7 +23,9 @@ interface BoardState {
canvasWidth: number // canvas width 0.1 ~ 1
canvasHeight: number // canvas height 0.1 ~ 1
backgroundColor: string // canvas background color
backgroundOpacity: number // canvas background opacity
backgroundOpacity: number // canvas background color opacity
hasBackgroundImage: boolean // canvas background image
backgroundImageOpacity: number // canvas background Image opacity
isObjectCaching: boolean // fabric objectCaching
openGuideLine: boolean // does the guide line show
}
Expand All @@ -33,6 +39,9 @@ interface BoardAction {
updateCanvasHeight: (height: number) => void
updateBackgroundColor: (color: string) => void
updateBackgroundOpacity: (opacity: number) => void
updateBackgroundImage: (image: string) => void
updateBackgroundImageOpacity: (opacity: number) => void
cleanBackgroundImage: () => void
updateCacheState: () => void
updateOpenGuideLine: () => void
}
Expand All @@ -51,6 +60,8 @@ const useBoardStore = create<BoardState & BoardAction>()(
canvasHeight: 1,
backgroundColor: 'rgba(255, 255, 255, 1)',
backgroundOpacity: 1,
hasBackgroundImage: false,
backgroundImageOpacity: 1,
isObjectCaching: true,
openGuideLine: false,
updateMode: (mode) => {
Expand Down Expand Up @@ -101,6 +112,21 @@ const useBoardStore = create<BoardState & BoardAction>()(
backgroundOpacity: 1
})
}

const backgroundImage = paintBoard?.canvas
?.backgroundImage as fabric.Image
if (backgroundImage) {
handleBackgroundImageWhenCanvasSizeChange()
set({
hasBackgroundImage: true,
backgroundOpacity: backgroundImage.opacity
})
} else {
set({
hasBackgroundImage: false,
backgroundOpacity: 1
})
}
},
updateCanvasWidth: (width) => {
const oldWidth = get().canvasWidth
Expand Down Expand Up @@ -146,6 +172,45 @@ const useBoardStore = create<BoardState & BoardAction>()(
return {}
})
},
updateBackgroundImage: (image) => {
const canvas = paintBoard.canvas
const oldBackgroundImage = canvas?.backgroundImage as fabric.Image
if (canvas && image !== oldBackgroundImage?.src) {
updateCanvasBackgroundImage(image)
set({
hasBackgroundImage: true
})
}
},
cleanBackgroundImage: () => {
set({
hasBackgroundImage: false
})
const canvas = paintBoard.canvas
if (canvas) {
canvas.setBackgroundImage(null as unknown as string, () => {
paintBoard.render()
})
}
},
updateBackgroundImageOpacity: (opacity) => {
set((state) => {
const canvas = paintBoard.canvas
if (canvas && opacity !== state.backgroundImageOpacity) {
const backgroundImage = canvas?.backgroundImage as fabric.Image
if (backgroundImage) {
backgroundImage.set({
opacity
})
}

return {
backgroundImageOpacity: opacity
}
}
return {}
})
},
updateCacheState() {
const oldCacheState = get().isObjectCaching
set({
Expand Down
3 changes: 2 additions & 1 deletion src/store/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export interface IBoardData {
version: string // fabric version
objects: fabric.Object[]
background: string // canvas background color (rgba)
backgroundImage: fabric.Image
}

interface IFile {
Expand Down Expand Up @@ -54,7 +55,7 @@ interface FileAction {
}

const initId = uuidv4()
export const BOARD_VERSION = '1.3.1'
export const BOARD_VERSION = '1.4.0'

const useFileStore = create<FileState & FileAction>()(
persist(
Expand Down
4 changes: 4 additions & 0 deletions src/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,8 @@ declare module 'fabric/fabric-impl' {
export interface Control {
pointIndex: number
}

export interface Image {
src: string
}
}
Loading

0 comments on commit 3cbba2c

Please sign in to comment.