Merge pull request #8 from Hufe921/feature/separator

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

@ -120,6 +120,31 @@
<div class="menu-item__hyperlink">
<i></i>
</div>
<div class="menu-item__separator">
<i></i>
<div class="options">
<ul>
<li data-separator='0,0'>
<i></i>
</li>
<li data-separator="1,1">
<i></i>
</li>
<li data-separator="3,1">
<i></i>
</li>
<li data-separator="4,4">
<i></i>
</li>
<li data-separator="7,3,3,3">
<i></i>
</li>
<li data-separator="6,2,2,2,2,2">
<i></i>
</li>
</ul>
</div>
</div>
</div>
<div class="menu-divider"></div>
<div class="menu-item">

@ -0,0 +1 @@
<svg width="126" height="20" xmlns="http://www.w3.org/2000/svg"><path d="M4 10h5v1H4zm7 0h2v1h-2zm4 0h2v1h-2zm4 0h5v1h-5zm7 0h2v1h-2zm4 0h2v1h-2zm4 0h5v1h-5zm7 0h2v1h-2zm4 0h2v1h-2zm4 0h5v1h-5zm7 0h2v1h-2zm4 0h2v1h-2zm4 0h5v1h-5zm7 0h2v1h-2zm4 0h2v1h-2zm4 0h5v1h-5zm7 0h2v1h-2zm4 0h2v1h-2zm4 0h5v1h-5zm7 0h2v1h-2zm4 0h2v1h-2zm4 0h5v1h-5zm7 0h2v1h-2zm4 0h2v1h-2z" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 390 B

@ -0,0 +1 @@
<svg width="126" height="20" xmlns="http://www.w3.org/2000/svg"><path d="M4 10h6v1H4zm9 0h3v1h-3zm6 0h6v1h-6zm9 0h3v1h-3zm6 0h6v1h-6zm9 0h3v1h-3zm6 0h6v1h-6zm9 0h3v1h-3zm6 0h6v1h-6zm9 0h3v1h-3zm6 0h6v1h-6zm9 0h3v1h-3zm6 0h6v1h-6zm9 0h3v1h-3zm6 0h6v1h-6zm9 0h3v1h-3z" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 294 B

@ -0,0 +1 @@
<svg width="126" height="20" xmlns="http://www.w3.org/2000/svg"><path d="M5 10h4v1H5zm8 0h4v1h-4zm8 0h4v1h-4zm8 0h4v1h-4zm8 0h4v1h-4zm8 0h4v1h-4zm8 0h4v1h-4zm8 0h4v1h-4zm8 0h4v1h-4zm8 0h4v1h-4zm8 0h4v1h-4zm8 0h4v1h-4zm8 0h4v1h-4zm8 0h4v1h-4zm8 0h4v1h-4z" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 282 B

@ -0,0 +1 @@
<svg width="126" height="20" xmlns="http://www.w3.org/2000/svg"><path d="M4 10h4v1H4zm5 0h4v1H9zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h3v1h-3z" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 389 B

@ -0,0 +1 @@
<svg width="126" height="20" xmlns="http://www.w3.org/2000/svg"><path d="M4 10h1v1H4zm12 0h1v1h-1zM6 10h1v1H6zm12 0h1v1h-1zM8 10h1v1H8zm12 0h1v1h-1zm-10 0h1v1h-1zm12 0h1v1h-1zm-10 0h1v1h-1zm12 0h1v1h-1zm-10 0h1v1h-1zm12 0h1v1h-1zm2 0h1v1h-1zm12 0h1v1h-1zm-10 0h1v1h-1zm12 0h1v1h-1zm-10 0h1v1h-1zm12 0h1v1h-1zm-10 0h1v1h-1zm12 0h1v1h-1zm-10 0h1v1h-1zm12 0h1v1h-1zm-10 0h1v1h-1zm12 0h1v1h-1zm2 0h1v1h-1zm12 0h1v1h-1zm-10 0h1v1h-1zm12 0h1v1h-1zm-10 0h1v1h-1zm12 0h1v1h-1zm-10 0h1v1h-1zm12 0h1v1h-1zm-10 0h1v1h-1zm12 0h1v1h-1zm-10 0h1v1h-1zm12 0h1v1h-1zm2 0h1v1h-1zm12 0h1v1h-1zm-10 0h1v1h-1zm12 0h1v1h-1zm-10 0h1v1h-1zm12 0h1v1h-1zm-10 0h1v1h-1zm12 0h1v1h-1zm-10 0h1v1h-1zm12 0h1v1h-1zm-10 0h1v1h-1zm12 0h1v1h-1zm2 0h1v1h-1zm12 0h1v1h-1zm-10 0h1v1h-1zm12 0h1v1h-1zm-10 0h1v1h-1zm12 0h1v1h-1zm-10 0h1v1h-1zm12 0h1v1h-1zm-10 0h1v1h-1zm12 0h1v1h-1zm-10 0h1v1h-1z" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 885 B

@ -0,0 +1 @@
<svg width="126" height="20" xmlns="http://www.w3.org/2000/svg"><path d="M4 10h118v1H4z" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 116 B

@ -0,0 +1 @@
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><path d="M3.9 2.8l2.01 2.506c.2.25.58.25.78 0L8.7 2.799l2.01 2.507c.2.25.58.25.78 0l2.4-2.993-.78-.626-2.01 2.507-2.01-2.507a.5.5 0 00-.78 0L6.3 4.194 4.29 1.687a.5.5 0 00-.78 0l-2.4 3 .78.625L3.9 2.8zM1 8h13v1H1zm0 4h3v1H1zm5 0h3v1H6zm5 0h3v1h-3z" fill="#3D4757"/></svg>

After

Width:  |  Height:  |  Size: 334 B

@ -40,6 +40,7 @@ export class Command {
private static cancelMergeTableCell: Function
private static image: Function
private static hyperlink: Function
private static separator: Function
private static search: Function
private static print: Function
private static pageScaleRecovery: Function
@ -82,6 +83,7 @@ export class Command {
Command.cancelMergeTableCell = adapt.cancelMergeTableCell.bind(adapt)
Command.image = adapt.image.bind(adapt)
Command.hyperlink = adapt.hyperlink.bind(adapt)
Command.separator = adapt.separator.bind(adapt)
Command.search = adapt.search.bind(adapt)
Command.print = adapt.print.bind(adapt)
Command.pageScaleRecovery = adapt.pageScaleRecovery.bind(adapt)
@ -233,6 +235,10 @@ export class Command {
return Command.image(payload)
}
public executeSeparator(payload: number[]) {
return Command.separator(payload)
}
public executeSearch(payload: string | null) {
return Command.search(payload)
}

@ -890,6 +890,36 @@ export class CommandAdapt {
this.draw.render({ curIndex })
}
public separator(payload: number[]) {
const { startIndex, endIndex } = this.range.getRange()
if (!~startIndex && !~endIndex) return
const elementList = this.draw.getElementList()
let curIndex = -1
// 光标存在分割线,则判断为修改线段逻辑
const endElement = elementList[endIndex + 1]
if (endElement && endElement.type === ElementType.SEPARATOR) {
if (endElement.dashArray && endElement.dashArray.join() === payload.join()) return
curIndex = endIndex
endElement.dashArray = payload
} else {
const newElement: IElement = {
value: '\n',
type: ElementType.SEPARATOR,
dashArray: payload
}
// 从行头增加分割线
if (startIndex !== 0 && elementList[startIndex].value === ZERO) {
elementList.splice(startIndex, 1, newElement)
curIndex = startIndex - 1
} else {
elementList.splice(startIndex + 1, 0, newElement)
curIndex = startIndex
}
}
this.range.setRange(curIndex, curIndex)
this.draw.render({ curIndex })
}
public image(payload: IDrawImagePayload) {
const { startIndex, endIndex } = this.range.getRange()
if (!~startIndex && !~endIndex) return

@ -30,6 +30,7 @@ import { HyperlinkParticle } from "./particle/HyperlinkParticle"
import { Header } from "./frame/Header"
import { SuperscriptParticle } from "./particle/Superscript"
import { SubscriptParticle } from "./particle/Subscript"
import { SeparatorParticle } from "./particle/Separator"
export class Draw {
@ -60,6 +61,7 @@ export class Draw {
private pageNumber: PageNumber
private header: Header
private hyperlinkParticle: HyperlinkParticle
private separatorParticle: SeparatorParticle
private superscriptParticle: SuperscriptParticle
private subscriptParticle: SubscriptParticle
@ -102,6 +104,7 @@ export class Draw {
this.pageNumber = new PageNumber(this)
this.header = new Header(this)
this.hyperlinkParticle = new HyperlinkParticle(this)
this.separatorParticle = new SeparatorParticle()
this.superscriptParticle = new SuperscriptParticle()
this.subscriptParticle = new SubscriptParticle()
@ -443,6 +446,12 @@ export class Draw {
metrics.height = elementHeight
metrics.boundingBoxDescent = elementHeight
metrics.boundingBoxAscent = 0
} else if (element.type === ElementType.SEPARATOR) {
element.width = innerWidth
metrics.width = innerWidth
metrics.height = this.options.defaultSize
metrics.boundingBoxAscent = -rowMargin
metrics.boundingBoxDescent = -rowMargin
} else {
// 设置上下标真实字体尺寸
const size = element.size || this.options.defaultSize
@ -570,6 +579,8 @@ export class Draw {
} else if (element.type === ElementType.SUBSCRIPT) {
this.textParticle.complete()
this.subscriptParticle.render(ctx, element, x, y + offsetY)
} else if (element.type === ElementType.SEPARATOR) {
this.separatorParticle.render(ctx, element, x, y)
} else {
this.textParticle.record(ctx, element, x, y + offsetY)
}

@ -0,0 +1,20 @@
import { IRowElement } from "../../../interface/Row"
export class SeparatorParticle {
public render(ctx: CanvasRenderingContext2D, element: IRowElement, x: number, y: number) {
ctx.save()
if (element.color) {
ctx.strokeStyle = element.color
}
if (element.dashArray && element.dashArray.length) {
ctx.setLineDash(element.dashArray)
}
ctx.translate(0, 0.5) // 从1处渲染避免线宽度等于3
ctx.beginPath()
ctx.moveTo(x, y)
ctx.lineTo(x + element.width!, y)
ctx.stroke()
ctx.restore()
}
}

@ -78,6 +78,7 @@ export class RangeManager {
const highlight = curElement.highlight || null
const rowFlex = curElement.rowFlex || null
const rowMargin = curElement.rowMargin || this.options.defaultRowMargin
const dashArray = curElement.dashArray || []
// 菜单
const painter = !!this.draw.getPainterStyle()
const undo = this.historyManager.isCanUndo()
@ -95,7 +96,8 @@ export class RangeManager {
color,
highlight,
rowFlex,
rowMargin
rowMargin,
dashArray
})
}
@ -119,7 +121,8 @@ export class RangeManager {
color: null,
highlight: null,
rowFlex: null,
rowMargin
rowMargin,
dashArray: []
})
}

@ -4,5 +4,6 @@ export enum ElementType {
TABLE = 'table',
HYPERLINK = 'hyperlink',
SUPERSCRIPT = 'superscript',
SUBSCRIPT = 'subscript'
SUBSCRIPT = 'subscript',
SEPARATOR = 'separator'
}

@ -47,7 +47,16 @@ export interface ISuperscriptSubscript {
actualSize?: number;
}
export type IElement = IElementBasic & IElementStyle & ITable & IHyperlinkElement & ISuperscriptSubscript
export interface ISeparator {
dashArray?: number[];
}
export type IElement = IElementBasic
& IElementStyle
& ITable
& IHyperlinkElement
& ISuperscriptSubscript
& ISeparator
export interface IElementMetrics {
width: number;

@ -16,6 +16,7 @@ export interface IRangeStyle {
highlight: string | null;
rowFlex: RowFlex | null;
rowMargin: number;
dashArray: number[];
}
export type IRangeStyleChange = (payload: IRangeStyle) => void

@ -63,7 +63,11 @@ window.onload = function () {
size: 16
}
})
data.splice(137, 0, {
data.splice(8, 0, {
value: '\n',
type: ElementType.SEPARATOR
})
data.splice(138, 0, {
type: ElementType.HYPERLINK,
value: '',
valueList: [{
@ -81,16 +85,16 @@ window.onload = function () {
}],
url: 'https://hufe.club/canvas-editor'
})
data.splice(378, 0, {
value: '',
data.splice(379, 0, {
value: '',
color: '#FF0000',
type: ElementType.SUBSCRIPT
})
data.splice(466, 0, {
data.splice(467, 0, {
value: '9',
type: ElementType.SUPERSCRIPT
})
data.splice(592, 0, {
data.splice(593, 0, {
value: `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFkAAAAgCAYAAAB5JtSmAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAQ0SURBVGhD7dhrUSNBFAVgvKACEVjAAhJQgAIUYAABGEAABvgfAdn6UnWou01PppOZhIXNj1P9vo9zH5PK1Waz2V5wWlxIPgMuJJ8Bi0h+fn7eXl9fb29ubrYPDw/dO/8DHh8fu/vB4kym4Orqaofb29vund8OSSbhemewSrugBMnG3vlvw9vb265yn56edmtz/t/f33+5C8MkixQSZSsl9UzLOHUmcwTYAN/Rpl5eXnY+pnIB0Xd3d7s5m3rvDsrkCGszNiQ7r/tr4v39fSc/uipOqRcqufTHBiO78GGdzG5xcLtIFmVde7L9NsvXRo9s84+Pj+79pUAwn5GcD1wIz5r+fYGeJdnjGiF9hwL7iWAcfX19/evtKVHJXrtN8Rf4A3TVczqhrut5i1mSZQgnIriSWtdzP2N+EvIhi3/GWqHWtWXuy2IYbheiKarJZIZknkxyrryc2Utrgal+9S8iScUXIx/3kcxfe/jotcuDezLFlIbARDrzHpytXdKnQr4xyc74Vu9YV5Ih2Q/tT7mDSEYw5ZU4wu3nJx64k/1z9umlUG0hah/JSbC6Jzi5exDJWoTHERoBxu8uf/pT1j3HDkUIJitjbRfRA/iwVzlgy1RCfSF5ili9xj7BUWKs9wJZ3MpditYu+lsc+/PRx53cVF9Pdg/syE9Hb6cS75PkmhUEUFofmTvLGEXKimHueJP9Y3swWQwGLUiA9xEbHKuvgs4pPe1+1myTAKlw81buJ8kigjAXKauXPLQPhEYgJSEYsgdTUR0BmTVgc6C359wcvKGnBrGO8dO5VlD1ZZ519nrBHvrwKVMCas9hgL0YUI2wV98fC4FqCWizzXyqF44A0ZKLHkilgvPs1zbiTuZIdZ414KvqGCKZYx4zple+MSrrJVncAyL02/TOqncJwVMglx5zI4QDZ5WPvBGEcNP+7TlEcqJIAQFGsIdQjmZt7MlYA5yiI3pOQTCQXUm2TuVmXgmewxDJQDgl6deJJoU5y7p9uwZagmu1mCvbNoOOBfkhOf6lRZjzPb8qRjBMMiUhM9GNMZQq5/oRXBP7Mlj/i12A7EMIaJGqDcl8I79+/N1xTvdINQ2TDAQSvI9Md479vdqCHKSFQKAfEmgBqCTDkjaSgOZXQkg2jy1ti0xApnBQJo/0obQRipeQXbN3CmxKGQch5xgki4Efghl/kFqzPD//2DnXIodIRpaoETaXxcmwGNO7N4I2Oyuc6b+xK/tL9IH3kY/E+r1JdST4yM+7VUiuJbuPZHBeHZcNvXtziMMV9mRuvUOX8Vg9IFjRx9dUYM3s2oJyNx9ahFfSWwyRHKHG3nmL2q/mojyFVAWnEdi2Hg7OBXwUCCKr1QEtoe0+/9jI3xqIiuF2QRD0zqcwpfQnge9TVSI4tWrNe79shj98F0xDC0N4bTUVF5LPgAvJJ8dm+wcP2iJuZNdC5QAAAABJRU5ErkJggg==`,
width: 89,
height: 32,
@ -460,6 +464,24 @@ window.onload = function () {
}
})
}
const separatorDom = document.querySelector<HTMLDivElement>('.menu-item__separator')!
const separatorOptionDom = separatorDom.querySelector<HTMLDivElement>('.options')!
separatorDom.onclick = function () {
console.log('separator')
separatorOptionDom.classList.toggle('visible')
}
separatorOptionDom.onmousedown = function (evt) {
let payload: number[] = []
const li = evt.target as HTMLLIElement
const separatorDash = li.dataset.separator?.split(',').map(Number)
if (separatorDash) {
const isSingleLine = separatorDash.every(d => d === 0)
if (!isSingleLine) {
payload = separatorDash
}
}
instance.command.executeSeparator(payload)
}
const collspanDom = 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 () {
@ -501,11 +523,22 @@ window.onload = function () {
// 控件类型
payload.type === ElementType.SUBSCRIPT ? subscriptDom.classList.add('active') : subscriptDom.classList.remove('active')
payload.type === ElementType.SUPERSCRIPT ? superscriptDom.classList.add('active') : superscriptDom.classList.remove('active')
payload.type === ElementType.SEPARATOR ? separatorDom.classList.add('active') : separatorDom.classList.remove('active')
separatorOptionDom.querySelectorAll('li').forEach(li => li.classList.remove('active'))
if (payload.type === ElementType.SEPARATOR) {
const separator = payload.dashArray.join(',') || '0,0'
const curSeparatorDom = separatorOptionDom.querySelector<HTMLLIElement>(`[data-separator='${separator}']`)!
if (curSeparatorDom) {
curSeparatorDom.classList.add('active')
}
}
// 富文本
const curFontDom = fontOptionDom.querySelector<HTMLLIElement>(`[data-family=${payload.font}]`)!
fontSelectDom.innerText = curFontDom.innerText
fontOptionDom.querySelectorAll<HTMLLIElement>('li').forEach(li => li.classList.remove('active'))
const curFontDom = fontOptionDom.querySelector<HTMLLIElement>(`[data-family=${payload.font}]`)
if (curFontDom) {
fontSelectDom.innerText = curFontDom.innerText
curFontDom.classList.add('active')
}
payload.bold ? boldDom.classList.add('active') : boldDom.classList.remove('active')
payload.italic ? italicDom.classList.add('active') : italicDom.classList.remove('active')
payload.underline ? underlineDom.classList.add('active') : underlineDom.classList.remove('active')

@ -369,6 +369,50 @@ ul {
background-image: url('./assets/images/hyperlink.svg');
}
.menu-item__separator {
position: relative;
}
.menu-item__separator>i {
background-image: url('./assets/images/separator.svg');
}
.menu-item .menu-item__separator .options {
width: 128px;
}
.menu-item .menu-item__separator li {
padding: 1px 5px;
}
.menu-item__separator li i {
pointer-events: none;
}
.menu-item__separator li[data-separator="0,0"] {
background-image: url('./assets/images/line-single.svg');
}
.menu-item__separator li[data-separator="1,1"] {
background-image: url('./assets/images/line-dot.svg');
}
.menu-item__separator li[data-separator="3,1"] {
background-image: url('./assets/images/line-dash-small-gap.svg');
}
.menu-item__separator li[data-separator="4,4"] {
background-image: url('./assets/images/line-dash-large-gap.svg');
}
.menu-item__separator li[data-separator="7,3,3,3"] {
background-image: url('./assets/images/line-dash-dot.svg');
}
.menu-item__separator li[data-separator="6,2,2,2,2,2"] {
background-image: url('./assets/images/line-dash-dot-dot.svg');
}
.menu-item__search {
position: relative;
}

Loading…
Cancel
Save