feat:add superscript and subscript

pr675
黄云飞 4 years ago
parent 8939d42295
commit ae3de7bf55

@ -57,6 +57,12 @@
<div class="menu-item__strikeout">
<i></i>
</div>
<div class="menu-item__superscript">
<i></i>
</div>
<div class="menu-item__subscript">
<i></i>
</div>
<div class="menu-item__color">
<i></i>
<span></span>

@ -0,0 +1 @@
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 16 16" xml:space="preserve"><style>.st0{fill:#3d4757}</style><g id="_x30_0-公共_x2F_02工具栏_x2F_subscript"><path id="_x32_" class="st0" d="M15 13.3v.7h-3c0-.3.1-.5.3-.8.2-.2.5-.6 1-1 .4-.3.6-.5.7-.7.1-.2.2-.3.2-.5s-.1-.3-.2-.4c-.1-.1-.2-.1-.4-.1s-.3 0-.4.1-.2.4-.2.7l-.9-.1c.1-.4.2-.7.5-.9.3-.2.6-.3 1-.3s.8.1 1 .3c.3.2.4.5.4.8 0 .2 0 .4-.1.5-.1.2-.2.3-.3.5-.1.1-.3.3-.6.5s-.4.4-.5.4c-.1.1-.1.2-.2.3H15z"/><path id="合并形状" class="st0" d="M7.2 8.5l4 5.5H9.8L6.5 9.4 3.2 14H1.8l4-5.5-4-5.5h1.4l3.3 4.6L9.8 3h1.3L7.2 8.5z"/></g></svg>

After

Width:  |  Height:  |  Size: 637 B

@ -0,0 +1 @@
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 16 16" xml:space="preserve"><style>.st0{fill:#3d4757}</style><g id="_x30_0-公共_x2F_02工具栏_x2F_superscript"><path id="_x32_" class="st0" d="M15 6.3V7h-3c0-.3.1-.5.3-.8.2-.2.5-.6 1-1 .3-.2.6-.5.7-.6.1-.2.2-.3.2-.5s-.1-.3-.2-.4c-.1-.1-.2-.1-.4-.1s-.3 0-.4.1-.2.3-.2.6l-.9-.1c.1-.4.2-.7.5-.9s.6-.3 1-.3.8.1 1 .3c.3.2.4.5.4.8 0 .2 0 .4-.1.5s-.2.4-.4.5c-.1.1-.3.3-.6.5s-.4.4-.4.5c-.1.1-.1.2-.2.3l1.7-.1z"/><path id="合并形状" class="st0" d="M7.4 8.5l4 5.5H10L6.7 9.4 3.4 14H2l4-5.5L2 3h1.4l3.3 4.6L10 3h1.3L7.4 8.5z"/></g></svg>

After

Width:  |  Height:  |  Size: 627 B

@ -20,6 +20,8 @@ export class Command {
private static italic: Function
private static underline: Function
private static strikeout: Function
private static superscript: Function
private static subscript: Function
private static color: Function
private static highlight: Function
private static left: Function
@ -60,6 +62,8 @@ export class Command {
Command.italic = adapt.italic.bind(adapt)
Command.underline = adapt.underline.bind(adapt)
Command.strikeout = adapt.strikeout.bind(adapt)
Command.superscript = adapt.superscript.bind(adapt)
Command.subscript = adapt.subscript.bind(adapt)
Command.color = adapt.color.bind(adapt)
Command.highlight = adapt.highlight.bind(adapt)
Command.left = adapt.rowFlex.bind(adapt)
@ -148,6 +152,14 @@ export class Command {
return Command.strikeout()
}
public executeSuperscript() {
return Command.superscript()
}
public executeSubscript() {
return Command.subscript()
}
public executeColor(payload: string) {
return Command.color(payload)
}

@ -1,5 +1,5 @@
import { ZERO } from "../../dataset/constant/Common"
import { EDITOR_ELEMENT_STYLE_ATTR } from "../../dataset/constant/Element"
import { EDITOR_ELEMENT_STYLE_ATTR, TEXTLIKE_ELEMENT_TYPE } from "../../dataset/constant/Element"
import { EditorContext } from "../../dataset/enum/Editor"
import { ElementType } from "../../dataset/enum/Element"
import { ElementStyleKey } from "../../dataset/enum/ElementStyle"
@ -181,6 +181,48 @@ export class CommandAdapt {
this.draw.render({ isSetCursor: false })
}
public superscript() {
const selection = this.range.getSelection()
if (!selection) return
const superscriptIndex = selection.findIndex(s => s.type === ElementType.SUPERSCRIPT)
selection.forEach(el => {
// 取消上标
if (~superscriptIndex) {
if (el.type === ElementType.SUPERSCRIPT) {
el.type = ElementType.TEXT
delete el.actualSize
}
} else {
// 设置上标
if (!el.type || el.type === ElementType.TEXT || el.type === ElementType.SUBSCRIPT) {
el.type = ElementType.SUPERSCRIPT
}
}
})
this.draw.render({ isSetCursor: false })
}
public subscript() {
const selection = this.range.getSelection()
if (!selection) return
const subscriptIndex = selection.findIndex(s => s.type === ElementType.SUBSCRIPT)
selection.forEach(el => {
// 取消下标
if (~subscriptIndex) {
if (el.type === ElementType.SUBSCRIPT) {
el.type = ElementType.TEXT
delete el.actualSize
}
} else {
// 设置下标
if (!el.type || el.type === ElementType.TEXT || el.type === ElementType.SUPERSCRIPT) {
el.type = ElementType.SUBSCRIPT
}
}
})
this.draw.render({ isSetCursor: false })
}
public color(payload: string) {
const selection = this.range.getSelection()
if (!selection) return
@ -911,8 +953,7 @@ export class CommandAdapt {
// 搜索文本
function searchClosure(payload: string | null, type: EditorContext, elementList: IElement[], restArgs?: ISearchResultRestArgs) {
if (!payload) return
const { TEXT, HYPERLINK } = ElementType
const text = elementList.map(e => !e.type || e.type === TEXT || e.type === HYPERLINK ? e.value : ZERO)
const text = elementList.map(e => !e.type || TEXTLIKE_ELEMENT_TYPE.includes(e.type) ? e.value : ZERO)
.filter(Boolean)
.join('')
const matchStartIndexList = []

@ -358,7 +358,9 @@ export class Draw {
private _getFont(el: IElement, scale: number = 1): string {
const { defaultSize, defaultFont } = this.options
return `${el.italic ? 'italic ' : ''}${el.bold ? 'bold ' : ''}${(el.size || defaultSize) * scale}px ${el.font || defaultFont}`
const font = el.font || defaultFont
const size = el.actualSize || el.size || defaultSize
return `${el.italic ? 'italic ' : ''}${el.bold ? 'bold ' : ''}${size * scale}px ${font}`
}
private _computeRowList(innerWidth: number, elementList: IElement[]) {
@ -442,7 +444,12 @@ export class Draw {
metrics.boundingBoxDescent = elementHeight
metrics.boundingBoxAscent = 0
} else {
metrics.height = (element.size || this.options.defaultSize) * scale
// 设置上下标真实字体尺寸
const size = element.size || this.options.defaultSize
if (element.type === ElementType.SUPERSCRIPT || element.type === ElementType.SUBSCRIPT) {
element.actualSize = Math.ceil(size * 0.6)
}
metrics.height = (element.actualSize || size) * scale
ctx.font = this._getFont(element)
const fontMetrics = this.textParticle.measureText(ctx, element)
metrics.width = fontMetrics.width * scale

@ -368,7 +368,7 @@ export class CanvasEvent {
if (!this.cursor) return
const cursorPosition = this.position.getCursorPosition()
if (!data || !cursorPosition || this.isCompositing) return
const { TEXT, HYPERLINK } = ElementType
const { TEXT, HYPERLINK, SUBSCRIPT, SUPERSCRIPT } = ElementType
const text = data.replaceAll(`\n`, ZERO)
const elementList = this.draw.getElementList()
const agentDom = this.cursor.getAgentDom()
@ -389,10 +389,13 @@ export class CanvasEvent {
value,
...restArg
}
const nextElement = elementList[endIndex + 1]
if (
element.type === TEXT
|| (!element.type && element.value !== ZERO)
|| (element.type === HYPERLINK && elementList[endIndex + 1]?.type === HYPERLINK)
|| (element.type === HYPERLINK && nextElement?.type === HYPERLINK)
|| (element.type === SUBSCRIPT && nextElement?.type === SUBSCRIPT)
|| (element.type === SUPERSCRIPT && nextElement?.type === SUPERSCRIPT)
) {
EDITOR_ELEMENT_COPY_ATTR.forEach(attr => {
const value = element[attr] as never

@ -1,3 +1,4 @@
import { ElementType } from "../.."
import { IEditorOption } from "../../interface/Editor"
import { IElement } from "../../interface/Element"
import { IRange } from "../../interface/Range"
@ -65,6 +66,8 @@ export class RangeManager {
}
const curElement = curElementList[curElementList.length - 1]
if (!curElement) return
// 类型
const type = curElement.type || ElementType.TEXT
// 富文本
const font = curElement.font || this.options.defaultFont
let bold = !~curElementList.findIndex(el => !el.bold)
@ -80,6 +83,7 @@ export class RangeManager {
const undo = this.historyManager.isCanUndo()
const redo = this.historyManager.isCanRedo()
this.listener.rangeStyleChange({
type,
undo,
redo,
painter,
@ -103,6 +107,7 @@ export class RangeManager {
const undo = this.historyManager.isCanUndo()
const redo = this.historyManager.isCanRedo()
this.listener.rangeStyleChange({
type: null,
undo,
redo,
painter,

@ -1,3 +1,4 @@
import { ElementType } from "../enum/Element"
import { IElement } from "../../interface/Element"
export const EDITOR_ELEMENT_STYLE_ATTR = [
@ -43,4 +44,11 @@ export const EDITOR_ELEMENT_ZIP_ATTR: Array<keyof IElement> = [
'url',
'colgroup',
'valueList'
]
export const TEXTLIKE_ELEMENT_TYPE: ElementType[] = [
ElementType.TEXT,
ElementType.HYPERLINK,
ElementType.SUBSCRIPT,
ElementType.SUPERSCRIPT
]

@ -43,7 +43,11 @@ export interface IHyperlinkElement {
export type ITable = ITableAttr & ITableElement
export type IElement = IElementBasic & IElementStyle & ITable & IHyperlinkElement
export interface ISuperscriptSubscript {
actualSize?: number;
}
export type IElement = IElementBasic & IElementStyle & ITable & IHyperlinkElement & ISuperscriptSubscript
export interface IElementMetrics {
width: number;

@ -1,7 +1,9 @@
import { ElementType } from ".."
import { RowFlex } from "../dataset/enum/Row"
import { IEditorResult } from "./Editor"
export interface IRangeStype {
type: ElementType | null;
undo: boolean;
redo: boolean;
painter: boolean;

@ -1,5 +1,6 @@
import { ElementType, IElement } from ".."
import { IElement } from ".."
import { ZERO } from "../dataset/constant/Common"
import { TEXTLIKE_ELEMENT_TYPE } from "../dataset/constant/Element"
export function writeText(text: string) {
if (!text) return
@ -7,9 +8,8 @@ export function writeText(text: string) {
}
export function writeTextByElementList(elementList: IElement[]) {
const { TEXT, HYPERLINK } = ElementType
const text = elementList
.map(p => !p.type || p.type === TEXT || p.type === HYPERLINK ? p.value : '')
.map(p => !p.type || TEXTLIKE_ELEMENT_TYPE.includes(p.type) ? p.value : '')
.join('')
writeText(text)
}

@ -83,12 +83,10 @@ window.onload = function () {
})
data.splice(383, 0, {
value: '轻',
size: 10,
type: ElementType.SUBSCRIPT
})
data.splice(392, 0, {
value: '急',
size: 10,
type: ElementType.SUPERSCRIPT
})
data.splice(594, 0, {
@ -273,6 +271,16 @@ window.onload = function () {
console.log('strikeout')
instance.command.executeStrikeout()
}
const superscriptDom = document.querySelector<HTMLDivElement>('.menu-item__superscript')!
superscriptDom.onclick = function () {
console.log('superscript')
instance.command.executeSuperscript()
}
const subscriptDom = document.querySelector<HTMLDivElement>('.menu-item__subscript')!
subscriptDom.onclick = function () {
console.log('subscript')
instance.command.executeSubscript()
}
const colorControlDom = document.querySelector<HTMLInputElement>('#color')!
colorControlDom.onchange = function () {
instance.command.executeColor(colorControlDom!.value)
@ -489,6 +497,9 @@ window.onload = function () {
// 内部事件监听
instance.listener.rangeStyleChange = function (payload) {
// 控件类型
payload.type === ElementType.SUBSCRIPT ? subscriptDom.classList.add('active') : subscriptDom.classList.remove('active')
payload.type === ElementType.SUPERSCRIPT ? superscriptDom.classList.add('active') : superscriptDom.classList.remove('active')
// 富文本
const curFontDom = fontOptionDom.querySelector<HTMLLIElement>(`[data-family=${payload.font}]`)!
fontSelectDom.innerText = curFontDom.innerText

@ -213,6 +213,14 @@ ul {
background-image: url('./assets/images/strikeout.svg');
}
.menu-item__superscript i {
background-image: url('./assets/images/superscript.svg');
}
.menu-item__subscript i {
background-image: url('./assets/images/subscript.svg');
}
.menu-item__color,
.menu-item__highlight {
display: flex;

Loading…
Cancel
Save