feat: add background image option
This commit is contained in:
@@ -27,7 +27,6 @@ interface IEditorOption {
|
|||||||
height?: number // Paper height. default: 1123
|
height?: number // Paper height. default: 1123
|
||||||
scale?: number // scaling. default: 1
|
scale?: number // scaling. default: 1
|
||||||
pageGap?: number // Paper spacing. default: 20
|
pageGap?: number // Paper spacing. default: 20
|
||||||
backgroundColor?: string // Paper background color. default: #FFFFFF
|
|
||||||
underlineColor?: string // Underline color. default: #000000
|
underlineColor?: string // Underline color. default: #000000
|
||||||
strikeoutColor?: string // Strikeout color. default: #FF0000
|
strikeoutColor?: string // Strikeout color. default: #FF0000
|
||||||
rangeColor?: string // Range color. default: #AECBFA
|
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}
|
group?: IGroup // Group option. {opacity?:number; backgroundColor?:string; activeOpacity?:number; activeBackgroundColor?:string; disabled?:boolean}
|
||||||
pageBreak?: IPageBreak // PageBreak option。{font?:string; fontSize?:number; lineDash?:number[];}
|
pageBreak?: IPageBreak // PageBreak option。{font?:string; fontSize?:number; lineDash?:number[];}
|
||||||
zone?: IZoneOption // Zone option。{tipDisabled?:boolean;}
|
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
|
height?: number // 纸张高度。默认:1123
|
||||||
scale?: number // 缩放比例。默认:1
|
scale?: number // 缩放比例。默认:1
|
||||||
pageGap?: number // 纸张间隔。默认:20
|
pageGap?: number // 纸张间隔。默认:20
|
||||||
backgroundColor?: string // 纸张背景色。默认:#FFFFFF
|
|
||||||
underlineColor?: string // 下划线颜色。默认:#000000
|
underlineColor?: string // 下划线颜色。默认:#000000
|
||||||
strikeoutColor?: string // 删除线颜色。默认:#FF0000
|
strikeoutColor?: string // 删除线颜色。默认:#FF0000
|
||||||
rangeColor?: string // 选区颜色。默认:#AECBFA
|
rangeColor?: string // 选区颜色。默认:#AECBFA
|
||||||
@@ -68,6 +67,7 @@ interface IEditorOption {
|
|||||||
group?: IGroup // 成组配置。{opacity?:number; backgroundColor?:string; activeOpacity?:number; activeBackgroundColor?:string; disabled?:boolean}
|
group?: IGroup // 成组配置。{opacity?:number; backgroundColor?:string; activeOpacity?:number; activeBackgroundColor?:string; disabled?:boolean}
|
||||||
pageBreak?: IPageBreak // 分页符配置。{font?:string; fontSize?:number; lineDash?:number[];}
|
pageBreak?: IPageBreak // 分页符配置。{font?:string; fontSize?:number; lineDash?:number[];}
|
||||||
zone?: IZoneOption // 编辑器区域配置。{tipDisabled?:boolean;}
|
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 { DeepRequired } from '../../../interface/Common'
|
||||||
import { IEditorOption } from '../../../interface/Editor'
|
import { IEditorOption } from '../../../interface/Editor'
|
||||||
import { Draw } from '../Draw'
|
import { Draw } from '../Draw'
|
||||||
@@ -5,19 +9,104 @@ import { Draw } from '../Draw'
|
|||||||
export class Background {
|
export class Background {
|
||||||
private draw: Draw
|
private draw: Draw
|
||||||
private options: DeepRequired<IEditorOption>
|
private options: DeepRequired<IEditorOption>
|
||||||
|
private imageCache: Map<string, HTMLImageElement>
|
||||||
|
|
||||||
constructor(draw: Draw) {
|
constructor(draw: Draw) {
|
||||||
this.draw = draw
|
this.draw = draw
|
||||||
this.options = draw.getOptions()
|
this.options = draw.getOptions()
|
||||||
|
this.imageCache = new Map()
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(ctx: CanvasRenderingContext2D, pageNo: number) {
|
private _renderBackgroundColor(
|
||||||
const { backgroundColor } = this.options
|
ctx: CanvasRenderingContext2D,
|
||||||
const width = this.draw.getCanvasWidth(pageNo)
|
color: string,
|
||||||
const height = this.draw.getCanvasHeight(pageNo)
|
width: number,
|
||||||
|
height: number
|
||||||
|
) {
|
||||||
ctx.save()
|
ctx.save()
|
||||||
ctx.fillStyle = backgroundColor
|
ctx.fillStyle = color
|
||||||
ctx.fillRect(0, 0, width, height)
|
ctx.fillRect(0, 0, width, height)
|
||||||
ctx.restore()
|
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 { deepClone, splitText } from './utils'
|
||||||
import { IZoneOption } from './interface/Zone'
|
import { IZoneOption } from './interface/Zone'
|
||||||
import { defaultZoneOption } from './dataset/constant/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 {
|
export default class Editor {
|
||||||
public command: Command
|
public command: Command
|
||||||
@@ -138,6 +141,10 @@ export default class Editor {
|
|||||||
...defaultZoneOption,
|
...defaultZoneOption,
|
||||||
...options.zone
|
...options.zone
|
||||||
}
|
}
|
||||||
|
const backgroundOptions: Required<IBackgroundOption> = {
|
||||||
|
...defaultBackground,
|
||||||
|
...options.background
|
||||||
|
}
|
||||||
|
|
||||||
const editorOptions: DeepRequired<IEditorOption> = {
|
const editorOptions: DeepRequired<IEditorOption> = {
|
||||||
mode: EditorMode.EDIT,
|
mode: EditorMode.EDIT,
|
||||||
@@ -153,7 +160,6 @@ export default class Editor {
|
|||||||
height: 1123,
|
height: 1123,
|
||||||
scale: 1,
|
scale: 1,
|
||||||
pageGap: 20,
|
pageGap: 20,
|
||||||
backgroundColor: '#FFFFFF',
|
|
||||||
underlineColor: '#000000',
|
underlineColor: '#000000',
|
||||||
strikeoutColor: '#FF0000',
|
strikeoutColor: '#FF0000',
|
||||||
rangeAlpha: 0.6,
|
rangeAlpha: 0.6,
|
||||||
@@ -194,7 +200,8 @@ export default class Editor {
|
|||||||
placeholder: placeholderOptions,
|
placeholder: placeholderOptions,
|
||||||
group: groupOptions,
|
group: groupOptions,
|
||||||
pageBreak: pageBreakOptions,
|
pageBreak: pageBreakOptions,
|
||||||
zone: zoneOptions
|
zone: zoneOptions,
|
||||||
|
background: backgroundOptions
|
||||||
}
|
}
|
||||||
// 数据处理
|
// 数据处理
|
||||||
data = deepClone(data)
|
data = deepClone(data)
|
||||||
@@ -292,7 +299,9 @@ export {
|
|||||||
ListType,
|
ListType,
|
||||||
ListStyle,
|
ListStyle,
|
||||||
WordBreak,
|
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,
|
PaperDirection,
|
||||||
WordBreak
|
WordBreak
|
||||||
} from '../dataset/enum/Editor'
|
} from '../dataset/enum/Editor'
|
||||||
|
import { IBackgroundOption } from './Background'
|
||||||
import { ICheckboxOption } from './Checkbox'
|
import { ICheckboxOption } from './Checkbox'
|
||||||
import { IPadding } from './Common'
|
import { IPadding } from './Common'
|
||||||
import { IControlOption } from './Control'
|
import { IControlOption } from './Control'
|
||||||
@@ -40,7 +41,6 @@ export interface IEditorOption {
|
|||||||
height?: number
|
height?: number
|
||||||
scale?: number
|
scale?: number
|
||||||
pageGap?: number
|
pageGap?: number
|
||||||
backgroundColor?: string
|
|
||||||
underlineColor?: string
|
underlineColor?: string
|
||||||
strikeoutColor?: string
|
strikeoutColor?: string
|
||||||
rangeColor?: string
|
rangeColor?: string
|
||||||
@@ -81,6 +81,7 @@ export interface IEditorOption {
|
|||||||
group?: IGroup
|
group?: IGroup
|
||||||
pageBreak?: IPageBreak
|
pageBreak?: IPageBreak
|
||||||
zone?: IZoneOption
|
zone?: IZoneOption
|
||||||
|
background?: IBackgroundOption
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IEditorResult {
|
export interface IEditorResult {
|
||||||
|
|||||||
Reference in New Issue
Block a user