Skip to content

Commit

Permalink
feat: cursor following page scrolling #229
Browse files Browse the repository at this point in the history
  • Loading branch information
Hufe921 committed Aug 4, 2023
1 parent a96a77a commit 3db28cc
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 7 deletions.
1 change: 1 addition & 0 deletions docs/en/guide/option.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ interface IEditorOption {
inactiveAlpha?: number // When the body content is out of focus, transparency. default: 0.6
historyMaxRecordCount: number // History (undo redo) maximum number of records. default: 100
printPixelRatio: number // Print the pixel ratio (larger values are clearer, but larger sizes). default: 3
maskMargin: IMargin // Masking margins above the editor(for example: menu bar, bottom toolbar)。default: [0, 0, 0, 0]
wordBreak: WordBreak // Word and punctuation breaks: No punctuation in the first line of the BREAK_WORD &The word is not split, and the line is folded after BREAK_ALL full according to the width of the character. default: BREAK_WORD
watermark?: IWatermark // Watermark{data:string; color?:string; opacity?:number; size?:number; font?:string;}
control?: IControlOption // Control {placeholderColor?:string; bracketColor?:string; prefix?:string; postfix?:string;}
Expand Down
1 change: 1 addition & 0 deletions docs/guide/option.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ interface IEditorOption {
inactiveAlpha?: number // 正文内容失焦时透明度。默认值:0.6
historyMaxRecordCount: number // 历史(撤销重做)最大记录次数。默认:100次
printPixelRatio: number // 打印像素比率(值越大越清晰,但尺寸越大)。默认:3
maskMargin: IMargin // 编辑器上的遮盖边距(如悬浮到编辑器上的菜单栏、底部工具栏)。默认:[0, 0, 0, 0]
wordBreak: WordBreak // 单词与标点断行:BREAK_WORD首行不出现标点&单词不拆分、BREAK_ALL按字符宽度撑满后折行。默认:BREAK_WORD
watermark?: IWatermark // 水印信息。{data:string; color?:string; opacity?:number; size?:number; font?:string;}
control?: IControlOption // 控件信息。 {placeholderColor?:string; bracketColor?:string; prefix?:string; postfix?:string;}
Expand Down
57 changes: 57 additions & 0 deletions src/editor/core/cursor/Cursor.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { CURSOR_AGENT_HEIGHT } from '../../dataset/constant/Cursor'
import { EDITOR_PREFIX } from '../../dataset/constant/Editor'
import { MoveDirection } from '../../dataset/enum/Observer'
import { DeepRequired } from '../../interface/Common'
import { ICursorOption } from '../../interface/Cursor'
import { IEditorOption } from '../../interface/Editor'
import { IElementPosition } from '../../interface/Element'
import { findScrollContainer } from '../../utils'
import { Draw } from '../draw/Draw'
import { CanvasEvent } from '../event/CanvasEvent'
import { Position } from '../position/Position'
Expand All @@ -15,6 +18,11 @@ export type IDrawCursorOption = ICursorOption & {
hitLineStartIndex?: number
}

export interface IMoveCursorToVisibleOption {
direction: MoveDirection
cursorPosition: IElementPosition
}

export class Cursor {
private readonly ANIMATION_CLASS = `${EDITOR_PREFIX}-cursor--animation`

Expand Down Expand Up @@ -153,4 +161,53 @@ export class Cursor {
this.cursorDom.style.display = 'none'
this._clearBlinkTimeout()
}

public moveCursorToVisible(payload: IMoveCursorToVisibleOption) {
const { cursorPosition, direction } = payload
if (!cursorPosition || !direction) return
const {
pageNo,
coordinate: { leftTop, leftBottom }
} = cursorPosition
// 当前页面距离滚动容器顶部距离
const prePageY =
pageNo * (this.draw.getHeight() + this.draw.getPageGap()) +
this.container.getBoundingClientRect().top
// 向上移动时:以顶部距离为准,向下移动时:以底部位置为准
const isUp = direction === MoveDirection.UP
const x = leftBottom[0]
const y = isUp ? leftTop[1] + prePageY : leftBottom[1] + prePageY
// 查找滚动容器,如果是滚动容器是document,则限制范围为当前窗口
const scrollContainer = findScrollContainer(this.container)
const rect = {
left: 0,
right: 0,
top: 0,
bottom: 0
}
if (scrollContainer === document.documentElement) {
rect.right = window.innerWidth
rect.bottom = window.innerHeight
} else {
const { left, right, top, bottom } =
scrollContainer.getBoundingClientRect()
rect.left = left
rect.right = right
rect.top = top
rect.bottom = bottom
}
// 可视范围根据参数调整
const { maskMargin } = this.options
rect.top += maskMargin[0]
rect.bottom -= maskMargin[2]
// 不在可视范围时,移动滚动条到合适位置
if (
!(x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom)
) {
const { scrollLeft, scrollTop } = scrollContainer
isUp
? scrollContainer.scroll(scrollLeft, scrollTop - (rect.top - y))
: scrollContainer.scroll(scrollLeft, scrollTop + y - rect.bottom)
}
}
}
18 changes: 12 additions & 6 deletions src/editor/core/event/handlers/keydown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { EditorZone } from '../../..'
import { ZERO } from '../../../dataset/constant/Common'
import { ElementType } from '../../../dataset/enum/Element'
import { KeyMap } from '../../../dataset/enum/KeyMap'
import { MoveDirection } from '../../../dataset/enum/Observer'
import { IElement, IElementPosition } from '../../../interface/Element'
import { formatElementContext } from '../../../utils/element'
import { isMod } from '../../../utils/hotkey'
Expand Down Expand Up @@ -196,7 +197,7 @@ export function keydown(evt: KeyboardEvent, host: CanvasEvent) {
} else if (evt.key === KeyMap.Up || evt.key === KeyMap.Down) {
if (isReadonly) return
let anchorPosition: IElementPosition = cursorPosition
const isUp = evt.key === KeyMap.Up
// 扩大选区时,判断移动光标点
if (evt.shiftKey) {
if (startIndex === cursorPosition.index) {
anchorPosition = positionList[endIndex]
Expand All @@ -213,14 +214,15 @@ export function keydown(evt: KeyboardEvent, host: CanvasEvent) {
rightTop: [curRightX]
}
} = anchorPosition
// 向上时在首行、向下时再最尾则忽略
// 向上时在首行、向下时在尾行则忽略
const isUp = evt.key === KeyMap.Up
if (
(isUp && rowIndex === 0) ||
(!isUp && rowIndex === draw.getRowCount() - 1)
) {
return
}
// 查找下一行信息
// 查找下一行位置列表
const probablePosition: IElementPosition[] = []
if (isUp) {
let p = index - 1
Expand Down Expand Up @@ -269,10 +271,9 @@ export function keydown(evt: KeyboardEvent, host: CanvasEvent) {
break
}
if (!nextIndex) return
const curIndex = nextIndex
// shift则缩放选区
let anchorStartIndex = curIndex
let anchorEndIndex = curIndex
let anchorStartIndex = nextIndex
let anchorEndIndex = nextIndex
if (evt.shiftKey) {
if (startIndex !== endIndex) {
if (startIndex === cursorPosition.index) {
Expand Down Expand Up @@ -300,6 +301,11 @@ export function keydown(evt: KeyboardEvent, host: CanvasEvent) {
isSubmitHistory: false,
isCompute: false
})
// 将光标移动到可视范围内
draw.getCursor().moveCursorToVisible({
cursorPosition: positionList[isUp ? anchorStartIndex : anchorEndIndex],
direction: isUp ? MoveDirection.UP : MoveDirection.DOWN
})
} else if (isMod(evt) && evt.key === KeyMap.Z) {
if (isReadonly) return
historyManager.undo()
Expand Down
1 change: 1 addition & 0 deletions src/editor/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ export default class Editor {
historyMaxRecordCount: 100,
wordBreak: WordBreak.BREAK_WORD,
printPixelRatio: 3,
maskMargin: [0, 0, 0, 0],
...options,
header: headerOptions,
footer: footerOptions,
Expand Down
1 change: 1 addition & 0 deletions src/editor/interface/Editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export interface IEditorOption {
inactiveAlpha?: number
historyMaxRecordCount?: number
printPixelRatio?: number
maskMargin?: IMargin
wordBreak?: WordBreak
header?: IHeader
footer?: IFooter
Expand Down
16 changes: 16 additions & 0 deletions src/editor/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,3 +219,19 @@ export function convertStringToBase64(input: string) {
const base64 = window.btoa(charArray.join(''))
return base64
}

export function findScrollContainer(element: HTMLElement) {
let parent = element.parentElement
while (parent) {
const style = window.getComputedStyle(parent)
const overflowY = style.getPropertyValue('overflow-y')
if (
parent.scrollHeight > parent.clientHeight &&
(overflowY === 'auto' || overflowY === 'scroll')
) {
return parent
}
parent = parent.parentElement
}
return document.documentElement
}
3 changes: 2 additions & 1 deletion src/mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -430,5 +430,6 @@ export const options: IEditorOption = {
},
placeholder: {
data: '请输入正文'
}
},
maskMargin: [60, 0, 30, 0] // 菜单栏高度60,底部工具栏30为遮盖层
}

0 comments on commit 3db28cc

Please sign in to comment.