feat: add background image option

pr675
Hufe921 2 years ago
parent 3253f3708e
commit eadf7f6e49

@ -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'
}

@ -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 {

Loading…
Cancel
Save