feat: add background image option
This commit is contained in:
@@ -27,7 +27,6 @@ interface IEditorOption {
|
||||
height?: number // Paper height. default: 1123
|
||||
scale?: number // scaling. default: 1
|
||||
pageGap?: number // Paper spacing. default: 20
|
||||
backgroundColor?: string // Paper background color. default: #FFFFFF
|
||||
underlineColor?: string // Underline color. default: #000000
|
||||
strikeoutColor?: string // Strikeout color. default: #FF0000
|
||||
rangeColor?: string // Range color. default: #AECBFA
|
||||
@@ -68,6 +67,7 @@ interface IEditorOption {
|
||||
group?: IGroup // Group option. {opacity?:number; backgroundColor?:string; activeOpacity?:number; activeBackgroundColor?:string; disabled?:boolean}
|
||||
pageBreak?: IPageBreak // PageBreak option。{font?:string; fontSize?:number; lineDash?:number[];}
|
||||
zone?: IZoneOption // Zone option。{tipDisabled?:boolean;}
|
||||
background?: IBackgroundOption // Background option. {color?:string; image?:string; size?:BackgroundSize; repeat?:BackgroundRepeat;}。default: {color: '#FFFFFF'}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ interface IEditorOption {
|
||||
height?: number // 纸张高度。默认:1123
|
||||
scale?: number // 缩放比例。默认:1
|
||||
pageGap?: number // 纸张间隔。默认:20
|
||||
backgroundColor?: string // 纸张背景色。默认:#FFFFFF
|
||||
underlineColor?: string // 下划线颜色。默认:#000000
|
||||
strikeoutColor?: string // 删除线颜色。默认:#FF0000
|
||||
rangeColor?: string // 选区颜色。默认:#AECBFA
|
||||
@@ -68,6 +67,7 @@ interface IEditorOption {
|
||||
group?: IGroup // 成组配置。{opacity?:number; backgroundColor?:string; activeOpacity?:number; activeBackgroundColor?:string; disabled?:boolean}
|
||||
pageBreak?: IPageBreak // 分页符配置。{font?:string; fontSize?:number; lineDash?:number[];}
|
||||
zone?: IZoneOption // 编辑器区域配置。{tipDisabled?:boolean;}
|
||||
background?: IBackgroundOption // 背景配置。{color?:string; image?:string; size?:BackgroundSize; repeat?:BackgroundRepeat;}。默认:{color: '#FFFFFF'}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import {
|
||||
BackgroundRepeat,
|
||||
BackgroundSize
|
||||
} from '../../../dataset/enum/Background'
|
||||
import { DeepRequired } from '../../../interface/Common'
|
||||
import { IEditorOption } from '../../../interface/Editor'
|
||||
import { Draw } from '../Draw'
|
||||
@@ -5,19 +9,104 @@ import { Draw } from '../Draw'
|
||||
export class Background {
|
||||
private draw: Draw
|
||||
private options: DeepRequired<IEditorOption>
|
||||
private imageCache: Map<string, HTMLImageElement>
|
||||
|
||||
constructor(draw: Draw) {
|
||||
this.draw = draw
|
||||
this.options = draw.getOptions()
|
||||
this.imageCache = new Map()
|
||||
}
|
||||
|
||||
public render(ctx: CanvasRenderingContext2D, pageNo: number) {
|
||||
const { backgroundColor } = this.options
|
||||
const width = this.draw.getCanvasWidth(pageNo)
|
||||
const height = this.draw.getCanvasHeight(pageNo)
|
||||
private _renderBackgroundColor(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
color: string,
|
||||
width: number,
|
||||
height: number
|
||||
) {
|
||||
ctx.save()
|
||||
ctx.fillStyle = backgroundColor
|
||||
ctx.fillStyle = color
|
||||
ctx.fillRect(0, 0, width, height)
|
||||
ctx.restore()
|
||||
}
|
||||
|
||||
private _drawImage(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
imageElement: HTMLImageElement,
|
||||
width: number,
|
||||
height: number
|
||||
) {
|
||||
const { background, scale } = this.options
|
||||
// contain
|
||||
if (background.size === BackgroundSize.CONTAIN) {
|
||||
const imageWidth = imageElement.width * scale
|
||||
const imageHeight = imageElement.height * scale
|
||||
if (
|
||||
!background.repeat ||
|
||||
background.repeat === BackgroundRepeat.NO_REPEAT
|
||||
) {
|
||||
ctx.drawImage(imageElement, 0, 0, imageWidth, imageHeight)
|
||||
} else {
|
||||
let startX = 0
|
||||
let startY = 0
|
||||
const repeatXCount =
|
||||
background.repeat === BackgroundRepeat.REPEAT ||
|
||||
background.repeat === BackgroundRepeat.REPEAT_X
|
||||
? Math.ceil((width * scale) / imageWidth)
|
||||
: 1
|
||||
const repeatYCount =
|
||||
background.repeat === BackgroundRepeat.REPEAT ||
|
||||
background.repeat === BackgroundRepeat.REPEAT_Y
|
||||
? Math.ceil((height * scale) / imageHeight)
|
||||
: 1
|
||||
for (let x = 0; x < repeatXCount; x++) {
|
||||
for (let y = 0; y < repeatYCount; y++) {
|
||||
ctx.drawImage(imageElement, startX, startY, imageWidth, imageHeight)
|
||||
startY += imageHeight
|
||||
}
|
||||
startY = 0
|
||||
startX += imageWidth
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// cover
|
||||
ctx.drawImage(imageElement, 0, 0, width * scale, height * scale)
|
||||
}
|
||||
}
|
||||
|
||||
private _renderBackgroundImage(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
width: number,
|
||||
height: number
|
||||
) {
|
||||
const { background } = this.options
|
||||
const imageElementCache = this.imageCache.get(background.image)
|
||||
if (imageElementCache) {
|
||||
this._drawImage(ctx, imageElementCache, width, height)
|
||||
} else {
|
||||
const img = new Image()
|
||||
img.setAttribute('crossOrigin', 'Anonymous')
|
||||
img.src = background.image
|
||||
img.onload = () => {
|
||||
this.imageCache.set(background.image, img)
|
||||
this._drawImage(ctx, img, width, height)
|
||||
// 避免层级上浮,触发编辑器二次渲染
|
||||
this.draw.render({
|
||||
isCompute: false,
|
||||
isSubmitHistory: false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public render(ctx: CanvasRenderingContext2D, pageNo: number) {
|
||||
const { background } = this.options
|
||||
if (background.image) {
|
||||
const { width, height } = this.options
|
||||
this._renderBackgroundImage(ctx, width, height)
|
||||
} else {
|
||||
const width = this.draw.getCanvasWidth(pageNo)
|
||||
const height = this.draw.getCanvasHeight(pageNo)
|
||||
this._renderBackgroundColor(ctx, background.color, width, height)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { IBackgroundOption } from '../../interface/Background'
|
||||
import { BackgroundRepeat, BackgroundSize } from '../enum/Background'
|
||||
|
||||
export const defaultBackground: Readonly<Required<IBackgroundOption>> = {
|
||||
color: '#FFFFFF',
|
||||
image: '',
|
||||
size: BackgroundSize.COVER,
|
||||
repeat: BackgroundRepeat.NO_REPEAT
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
export enum BackgroundSize {
|
||||
CONTAIN = 'contain',
|
||||
COVER = 'cover'
|
||||
}
|
||||
|
||||
export enum BackgroundRepeat {
|
||||
REPEAT = 'repeat',
|
||||
NO_REPEAT = 'no-repeat',
|
||||
REPEAT_X = 'repeat-x',
|
||||
REPEAT_Y = 'repeat-y'
|
||||
}
|
||||
+12
-3
@@ -75,6 +75,9 @@ import { IRange } from './interface/Range'
|
||||
import { deepClone, splitText } from './utils'
|
||||
import { IZoneOption } from './interface/Zone'
|
||||
import { defaultZoneOption } from './dataset/constant/Zone'
|
||||
import { IBackgroundOption } from './interface/Background'
|
||||
import { defaultBackground } from './dataset/constant/Background'
|
||||
import { BackgroundRepeat, BackgroundSize } from './dataset/enum/Background'
|
||||
|
||||
export default class Editor {
|
||||
public command: Command
|
||||
@@ -138,6 +141,10 @@ export default class Editor {
|
||||
...defaultZoneOption,
|
||||
...options.zone
|
||||
}
|
||||
const backgroundOptions: Required<IBackgroundOption> = {
|
||||
...defaultBackground,
|
||||
...options.background
|
||||
}
|
||||
|
||||
const editorOptions: DeepRequired<IEditorOption> = {
|
||||
mode: EditorMode.EDIT,
|
||||
@@ -153,7 +160,6 @@ export default class Editor {
|
||||
height: 1123,
|
||||
scale: 1,
|
||||
pageGap: 20,
|
||||
backgroundColor: '#FFFFFF',
|
||||
underlineColor: '#000000',
|
||||
strikeoutColor: '#FF0000',
|
||||
rangeAlpha: 0.6,
|
||||
@@ -194,7 +200,8 @@ export default class Editor {
|
||||
placeholder: placeholderOptions,
|
||||
group: groupOptions,
|
||||
pageBreak: pageBreakOptions,
|
||||
zone: zoneOptions
|
||||
zone: zoneOptions,
|
||||
background: backgroundOptions
|
||||
}
|
||||
// 数据处理
|
||||
data = deepClone(data)
|
||||
@@ -292,7 +299,9 @@ export {
|
||||
ListType,
|
||||
ListStyle,
|
||||
WordBreak,
|
||||
ControlIndentation
|
||||
ControlIndentation,
|
||||
BackgroundRepeat,
|
||||
BackgroundSize
|
||||
}
|
||||
|
||||
// 对外类型
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { BackgroundRepeat, BackgroundSize } from '../dataset/enum/Background'
|
||||
|
||||
export interface IBackgroundOption {
|
||||
color?: string
|
||||
image?: string
|
||||
size?: BackgroundSize
|
||||
repeat?: BackgroundRepeat
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
PaperDirection,
|
||||
WordBreak
|
||||
} from '../dataset/enum/Editor'
|
||||
import { IBackgroundOption } from './Background'
|
||||
import { ICheckboxOption } from './Checkbox'
|
||||
import { IPadding } from './Common'
|
||||
import { IControlOption } from './Control'
|
||||
@@ -40,7 +41,6 @@ export interface IEditorOption {
|
||||
height?: number
|
||||
scale?: number
|
||||
pageGap?: number
|
||||
backgroundColor?: string
|
||||
underlineColor?: string
|
||||
strikeoutColor?: string
|
||||
rangeColor?: string
|
||||
@@ -81,6 +81,7 @@ export interface IEditorOption {
|
||||
group?: IGroup
|
||||
pageBreak?: IPageBreak
|
||||
zone?: IZoneOption
|
||||
background?: IBackgroundOption
|
||||
}
|
||||
|
||||
export interface IEditorResult {
|
||||
|
||||
Reference in New Issue
Block a user