Merge pull request #9 from Hufe921/feature/watermark

feature/watermark
pr675
Hufe 4 years ago committed by GitHub
commit 3f23691cc9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -145,6 +145,15 @@
</ul>
</div>
</div>
<div class="menu-item__watermark">
<i></i>
<div class="options">
<ul>
<li data-menu="add">添加水印</li>
<li data-menu="delete">删除水印</li>
</ul>
</div>
</div>
</div>
<div class="menu-divider"></div>
<div class="menu-item">

@ -0,0 +1 @@
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><rect stroke="#3D4757" x="2.5" y="1.5" width="11" height="13" rx="1"/><path d="M4 7h8v1H4V7zm0-2h8v1H4V5zm0 6h8v1H4v-1zm0-2h8v1H4V9z" fill="#909AA9" fill-rule="nonzero"/><path d="M6 5.623c.36-.354.936-.638 2-.604 1.785.057 2-.008 2 1.894v4.098H8.984v-.993c1.046.662.885.993-.484.993-1.792 0-2.5-1.068-2.5-2 0-1.427.447-2 2.5-2h.484c0-1-.006-1.007-.984-1-.978.008-1.419.437-2 .437v-.825zm2.5 2.389c-.899 0-1.5-.232-1.5 1 0 1.31.438 1 1.5 1 .617 0 .484-.665.484-1.848v-.152H8.5z" fill="#3D4757"/></g></svg>

After

Width:  |  Height:  |  Size: 602 B

@ -55,6 +55,7 @@
height: 30px;
display: flex;
align-items: center;
justify-content: space-between;
}
.dialog-option__item span {

@ -1,6 +1,7 @@
import { IElement } from "../.."
import { RowFlex } from "../../dataset/enum/Row"
import { IDrawImagePayload } from "../../interface/Draw"
import { IWatermark } from "../../interface/Watermark"
import { CommandAdapt } from "./CommandAdapt"
export class Command {
@ -41,6 +42,8 @@ export class Command {
private static image: Function
private static hyperlink: Function
private static separator: Function
private static addWatermark: Function
private static deleteWatermark: Function
private static search: Function
private static print: Function
private static pageScaleRecovery: Function
@ -84,6 +87,8 @@ export class Command {
Command.image = adapt.image.bind(adapt)
Command.hyperlink = adapt.hyperlink.bind(adapt)
Command.separator = adapt.separator.bind(adapt)
Command.addWatermark = adapt.addWatermark.bind(adapt)
Command.deleteWatermark = adapt.deleteWatermark.bind(adapt)
Command.search = adapt.search.bind(adapt)
Command.print = adapt.print.bind(adapt)
Command.pageScaleRecovery = adapt.pageScaleRecovery.bind(adapt)
@ -239,6 +244,14 @@ export class Command {
return Command.separator(payload)
}
public executeAddWatermark(payload: IWatermark) {
return Command.addWatermark(payload)
}
public executeDeleteWatermark() {
return Command.deleteWatermark()
}
public executeSearch(payload: string | null) {
return Command.search(payload)
}

@ -1,5 +1,6 @@
import { ZERO } from "../../dataset/constant/Common"
import { EDITOR_ELEMENT_STYLE_ATTR, TEXTLIKE_ELEMENT_TYPE } from "../../dataset/constant/Element"
import { defaultWatermarkOption } from "../../dataset/constant/Watermark"
import { EditorContext } from "../../dataset/enum/Editor"
import { ElementType } from "../../dataset/enum/Element"
import { ElementStyleKey } from "../../dataset/enum/ElementStyle"
@ -11,6 +12,7 @@ import { ISearchResult, ISearchResultRestArgs } from "../../interface/Search"
import { IColgroup } from "../../interface/table/Colgroup"
import { ITd } from "../../interface/table/Td"
import { ITr } from "../../interface/table/Tr"
import { IWatermark } from "../../interface/Watermark"
import { getUUID } from "../../utils"
import { formatElementList } from "../../utils/element"
import { printImageBase64 } from "../../utils/print"
@ -920,6 +922,31 @@ export class CommandAdapt {
this.draw.render({ curIndex })
}
public addWatermark(payload: IWatermark) {
const options = this.draw.getOptions()
const { color, size, opacity, font } = defaultWatermarkOption
options.watermark.data = payload.data
options.watermark.color = payload.color || color
options.watermark.size = payload.size || size
options.watermark.opacity = payload.opacity || opacity
options.watermark.font = payload.font || font
this.draw.render({
isSetCursor: false,
isSubmitHistory: false
})
}
public deleteWatermark() {
const options = this.draw.getOptions()
if (options.watermark && options.watermark.data) {
options.watermark = { ...defaultWatermarkOption }
this.draw.render({
isSetCursor: false,
isSubmitHistory: false
})
}
}
public image(payload: IDrawImagePayload) {
const { startIndex, endIndex } = this.range.getRange()
if (!~startIndex && !~endIndex) return

@ -31,6 +31,7 @@ import { Header } from "./frame/Header"
import { SuperscriptParticle } from "./particle/Superscript"
import { SubscriptParticle } from "./particle/Subscript"
import { SeparatorParticle } from "./particle/Separator"
import { Watermark } from "./frame/Watermark"
export class Draw {
@ -59,6 +60,7 @@ export class Draw {
private tableParticle: TableParticle
private tableTool: TableTool
private pageNumber: PageNumber
private waterMark: Watermark
private header: Header
private hyperlinkParticle: HyperlinkParticle
private separatorParticle: SeparatorParticle
@ -102,6 +104,7 @@ export class Draw {
this.tableParticle = new TableParticle(this)
this.tableTool = new TableTool(this)
this.pageNumber = new PageNumber(this)
this.waterMark = new Watermark(this)
this.header = new Header(this)
this.hyperlinkParticle = new HyperlinkParticle(this)
this.separatorParticle = new SeparatorParticle()
@ -697,6 +700,10 @@ export class Draw {
if (this.searchMatchList) {
this.search.render(ctx, pageNo)
}
// 绘制水印
if (this.options.watermark.data) {
this.waterMark.render(ctx)
}
}
public render(payload?: IDrawOption) {

@ -0,0 +1,33 @@
import { IEditorOption } from "../../.."
import { DeepRequired } from "../../../interface/Common"
import { Draw } from "../Draw"
export class Watermark {
private draw: Draw
private options: DeepRequired<IEditorOption>
constructor(draw: Draw) {
this.draw = draw
this.options = <DeepRequired<IEditorOption>>draw.getOptions()
}
public render(ctx: CanvasRenderingContext2D) {
const { watermark: { data, opacity, font, size, color }, scale } = this.options
const width = this.draw.getWidth()
const height = this.draw.getHeight()
const x = width / 2
const y = height / 2
ctx.save()
ctx.globalAlpha = opacity
ctx.font = `${size * scale}px ${font}`
ctx.fillStyle = color
// 移动到中心位置再旋转
const measureText = ctx.measureText(data)
ctx.translate(x, y)
ctx.rotate(-45 * Math.PI / 180)
ctx.fillText(data, - measureText.width / 2, measureText.actualBoundingBoxAscent - size / 2)
ctx.restore()
}
}

@ -462,7 +462,7 @@ export class CanvasEvent {
public save(): IEditorResult {
// 配置
const { width, height, margins } = this.draw.getOptions()
const { width, height, margins, watermark } = this.draw.getOptions()
// 数据
const elementList = this.draw.getOriginalElementList()
const data = zipElementList(elementList)
@ -471,6 +471,7 @@ export class CanvasEvent {
width,
height,
margins,
watermark: watermark.data ? watermark : undefined,
data
}
}

@ -0,0 +1,8 @@
import { IHeader } from "../../interface/Header"
export const defaultHeaderOption: Readonly<Required<IHeader>> = {
data: '',
color: '#AAAAAA',
size: 14,
font: 'Yahei'
}

@ -0,0 +1,9 @@
import { IWatermark } from "../../interface/Watermark"
export const defaultWatermarkOption: Readonly<Required<IWatermark>> = {
data: '',
color: '#AEB5C0',
opacity: 0.3,
size: 200,
font: 'Yahei'
}

@ -16,6 +16,9 @@ import { IContextMenuContext, IRegisterContextMenu } from './interface/contextme
import { EditorComponent } from './dataset/enum/Editor'
import { EDITOR_COMPONENT } from './dataset/constant/Editor'
import { IHeader } from './interface/Header'
import { IWatermark } from './interface/Watermark'
import { defaultHeaderOption } from './dataset/constant/Header'
import { defaultWatermarkOption } from './dataset/constant/Watermark'
export default class Editor {
@ -25,12 +28,13 @@ export default class Editor {
constructor(container: HTMLDivElement, elementList: IElement[], options: IEditorOption = {}) {
const headerOptions: Required<IHeader> = {
data: '',
color: '#AAAAAA',
size: 14,
font: 'Yahei',
...defaultHeaderOption,
...options.header
}
const waterMarkOptions: Required<IWatermark> = {
...defaultWatermarkOption,
...options.watermark
}
const editorOptions: Required<IEditorOption> = {
defaultType: 'TEXT',
defaultFont: 'Yahei',
@ -62,7 +66,8 @@ export default class Editor {
defaultHyperlinkColor: '#0000FF',
headerTop: 50,
...options,
header: headerOptions
header: headerOptions,
watermark: waterMarkOptions
}
formatElementList(elementList)
// 监听

@ -1,5 +1,6 @@
import { IElement } from ".."
import { IHeader } from "./Header"
import { IWatermark } from "./Watermark"
export interface IEditorOption {
defaultType?: string;
@ -32,6 +33,7 @@ export interface IEditorOption {
defaultHyperlinkColor?: string;
headerTop?: number;
header?: IHeader;
watermark?: IWatermark;
}
export interface IEditorResult {
@ -39,5 +41,6 @@ export interface IEditorResult {
width: number;
height: number;
margins: [top: number, right: number, bottom: number, left: number];
watermark?: IWatermark;
data: IElement[];
}

@ -0,0 +1,7 @@
export interface IWatermark {
data: string;
color?: string;
opacity?: number;
size?: number;
font?: string;
}

@ -211,6 +211,10 @@ window.onload = function () {
margins: [100, 120, 100, 120],
header: {
data: '人民医院门诊'
},
watermark: {
data: 'CANVAS-EDITOR',
size: 120
}
})
console.log('实例: ', instance)
@ -482,6 +486,53 @@ window.onload = function () {
}
instance.command.executeSeparator(payload)
}
const watermarkDom = document.querySelector<HTMLDivElement>('.menu-item__watermark')!
const watermarkOptionDom = watermarkDom.querySelector<HTMLDivElement>('.options')!
watermarkDom.onclick = function () {
console.log('watermark')
watermarkOptionDom.classList.toggle('visible')
}
watermarkOptionDom.onmousedown = function (evt) {
const li = evt.target as HTMLLIElement
const menu = li.dataset.menu!
watermarkOptionDom.classList.toggle('visible')
if (menu === 'add') {
new Dialog({
title: '水印',
data: [{
type: 'text',
label: '内容',
name: 'data',
placeholder: '请输入内容'
}, {
type: 'color',
label: '颜色',
name: 'color',
value: '#AEB5C0'
}, {
type: 'number',
label: '字体大小',
name: 'size',
value: '120'
}],
onConfirm: (payload) => {
const nullableIndex = payload.findIndex(p => !p.value)
if (~nullableIndex) return
const watermark = payload.reduce((pre, cur) => {
pre[cur.name] = cur.value
return pre
}, <any>{})
instance.command.executeAddWatermark({
data: watermark.data,
color: watermark.color,
size: Number(watermark.size)
})
}
})
} else {
instance.command.executeDeleteWatermark()
}
}
const searchCollapseDom = document.querySelector<HTMLDivElement>('.menu-item__search__collapse')
const searchInputDom = document.querySelector<HTMLInputElement>('.menu-item__search__collapse__search input')
document.querySelector<HTMLDivElement>('.menu-item__search')!.onclick = function () {

@ -413,6 +413,14 @@ ul {
background-image: url('./assets/images/line-dash-dot-dot.svg');
}
.menu-item__watermark>i {
background-image: url('./assets/images/watermark.svg');
}
.menu-item__watermark {
position: relative;
}
.menu-item__search {
position: relative;
}

Loading…
Cancel
Save