предисловие
Расширенный текстовый контент предназначен для увеличения ввода стиля, Quill предоставляет способ определить API для изменения DOM, в отличие от многих других редакторов, которые делают это путем обхода дерева DOM. Поддержка Quill и возможности настройки, следующие за следующим, чтобы знатьКак использовать Quill и связанный с ним исходный код.
quickstart
Подобно Vue и React, Quill также требует наличия существующего элемента dom в качестве цели для монтирования экземпляра Quill.
Базовая конфигурация
Параметр 1: может быть dom, может быть селектором (автоматически преобразуется в соответствующий dom). Параметр 2: объект конфигурации, некоторые параметры конфигурации для форматированного текста.
var editor = new Quill('#editor', options);
объект конфигурации опций
var options = {
modules: {
toolbar: '#toolbar'
},
placeholder: 'Compose an epic...',
theme: 'snow'
};
modules.toolbar
Используется для настройки панели инструментов
Способ 1: css-теги
var quill = new Quill('#editor', {
modules: {
// Equivalent to { toolbar: { container: '#toolbar' }}
toolbar: '#toolbar'
}
});
Способ 2: Объект
var quill = new Quill('#editor', {
modules: {
toolbar: {
container:'#toolbar'
}
}
});
Способ 3: Массив
var quill = new Quill('#editor', {
modules: {
toolbar: ['bold', 'italic', 'underline', 'strike']
}
});
Область редактирования и область панели инструментов
Как видно из этих конфигураций, весь форматированный текст разделен на: область редактирования и область панели инструментов.#editor
Цель смонтированного элемента будет заменена частью поля ввода, и#toolbar
Цель смонтированного элемента будет заменена на: Область панели инструментов.
var editor = new Quill('#editor', {
modules: {
toolbar: {
container:'#toolbar'
}
}
});
Настройка
Благодаря приведенной выше простой конфигурации вы можете использовать некоторые основные функции редактора форматированного текста для настройки функции Quill.
Как реализованы собственные функции Quill
Функция шрифта реализуется в два этапа:
- Создайте экземпляр класса Parchment.Attributor.Class. В предыдущем введении мы знали, что существует базовый класс под атрибутом Parchment.Attributor.
Attributor
и три функциональных классаclass
,style
,store
- Мы создаем узел blots через класс, затем нам нужны зарегистрированные узлы.
// 步骤1
import Parchment from 'parchment';
let config = {
scope: Parchment.Scope.INLINE,
whitelist: ['serif', 'monospace']
};
let FontClass = new Parchment.Attributor.Class('font', 'ql-font', config);
export { FontClass };
// 步骤2
import { FontClass } from './formats/font';
Quill.register({
'formats/font': FontClass,
},true)
Пользовательская функция высоты строки
Сам Quill не поддерживает функцию высоты строки, нам нужно настроить эту функцию, реализация функции имитации локального шрифта Quill выглядит следующим образом:демонстрация пользовательской высоты строки codepen
const config = {
scope: Parchment.Scope.INLINE,
whitelist: this.lineHeightList
}
const Parchment = Quill.import("parchment");
// 步骤一实例化 lineHeight 类(自定义的)
class lineHeightAttributor extends Parchment.Attributor.Class {}
const lineHeightStyle = new lineHeightAttributor(
"lineHeight",
"ql-lineHeight",
config
);
// 注册实例
Quill.register({ "formats/lineHeight": lineHeightStyle }, true);
После регистрации экземпляра, как использовать функцию высоты строки?
- Поскольку мы наследуем
Parchment.Attributor.Class
Он будет управлять стилем узла, манипулируя именем класса узла, поэтому его можно настроить с помощьюql-lineHeight
класс для управления选中文本的行高
<div id="app">
<div id="toolbar">
<select class="ql-lineHeight">
// 通过 selected 来设置默认选中的行高样式
<option v-for="(lineHeight,index) in lineHeightList" :key="lineHeight" :value="lineHeight" :selected="index === 3">{{ lineHeight }}</option>
</select>
</div>
<div id="container"></div>
</div>
- Поскольку структура DOM определила определение будущего, нам нужно настроить эти стили высокой соответствующей строки,
// 该类名对应的行高
.ql-lineHeight-2 {
line-height: 2;
}
// 设置 option 下拉框中选项的样式
.ql-picker.ql-lineHeight .ql-picker-label[data-value="2"]::before,
// 设置 选中 option 后显示的样式
.ql-picker.ql-lineHeight .ql-picker-item[data-value="2"]::before {
content: "2";
}
пользовательский размер шрифта
- Тип подэлемента белого списка должен быть типа String, иначе соответствующее имя класса не будет применено после выбора подэлемента.
data(){
return {
// whitelist 子项类型必须为 String 类型,否则会导致选中子项后没有应用上对应都类名
sizeList:Array.from(Array(58),(item,index)=>String(index+12)),
}
}
const Parchment = Quill.import("parchment")
class Font extends Parchment.Attributor.Class{}
const FontStyle = new Font('size','ql-size',{
scope:Parchment.Scope.INLINE,
whitelist:this.sizeList
})
Quill.register({
'formats/size':FontStyle
},true)
// 使用 less 中 range 和 each 方法来减少重复 css 代码
@list:range(11,70,1);
each(@list,{
.ql-size-@{value}{
font-size:@value*1px;
}
})
Более подробный код в демо можноНажмите, чтобы просмотреть
мероприятие
событие изменения текста
this.quill.on('text-change',function(delta,oldDelta,source){
console.log('delata',delta,oldDelta,source);
})
-
Вы можете видеть, что объект дельта-данных содержит:
- сохранить: сколько цифр данных сохранить перед
- вставка: вставленные данные
-
источник указывает источник триггера события, если он инициирован пользователем, это
user
Если операция APIapi
другие события
тип события: можно пройтиon(name: String, handler: Function): Quill
quill.on() для регистрации событий, таких как:text-change
,editor-change
Добавить пользовательские события:
// 获取到 toolbar 操作对象
let toolbar = quill.getModule('toolbar');
toolbar.addHandler('image', ()=>{
// 添加点击图片触发的逻辑
});
Исходный код Quill
Узнайте, как использовать Quill из фонда и как настроить некоторые функции, а затем просмотреть структуру исходного кода Quill.
Конструктор
путь/core/quill.js
, Вы можете видеть, что многие статические методы и методы-прототипы монтируются в конструкторе, чтобы понять реализацию некоторых часто используемых методов.
class Quill {
static debug(limit) {
if (limit === true) {
limit = 'log';
}
logger.level(limit);
}
static find(node) {
return node.__quill || Parchment.find(node);
}
static import(name) {
if (this.imports[name] == null) {
debug.error(`Cannot import ${name}. Are you sure it was registered?`);
}
return this.imports[name];
}
static register(path, target, overwrite = false) {
}
// ...
}
Quill.import
Например, мы можем пройтиQuill.import
для вызова статического методаimport
, выполнитьthis.imports[name]
Следующие четыре модуля по умолчанию монтируются в объекте Quill.imports.
Quill.imports = {
'delta' : Delta,
'parchment' : Parchment,
'core/module' : Module,
'core/theme' : Theme
};
Quill.register
Если вы хотите добавить дополнительные модули, вы можете пройтиregister
способ регистрации новых путей и соответствующих модулей,
Когда мы используем код, обычно выглядит следующим образом
class LinkBlot extends Inline {}
LinkBlot.blotName = 'link';
LinkBlot.tagName = 'a';
Quill.register(LinkBlot);
Давайте посмотрим на выполнение в исходном коде Quill, порядок выполнения в следующих методах
this.register('formats/' + 'link', LinkBlot, undefined);
- this.imports['formats/link'] = LinkBlot
- Наконец-то зарегистрировал модуль через Parchment.register(LinkBlot)
class Quill{
static register(path, target, overwrite = false) {
if (typeof path !== 'string') {
let name = path.attrName || path.blotName;
if (typeof name === 'string') {
// register(Blot | Attributor, overwrite)
this.register('formats/' + name, path, target);
} else {
Object.keys(path).forEach((key) => {
this.register(key, path[key], target);
});
}
}else {
if (this.imports[path] != null && !overwrite) {
debug.warn(`Overwriting ${path} with`, target);
}
this.imports[path] = target;
if ((path.startsWith('blots/') || path.startsWith('formats/')) &&
target.blotName !== 'abstract') {
Parchment.register(target);
} else if (path.startsWith('modules') && typeof target.register === 'function') {
target.register();
}
}
}
}
Внутренняя реализация функции шрифта Quill
Делится на два этапа:
- Создайте экземпляр класса Parchment.Attributor.Class. В предыдущем введении мы знали, что существует базовый класс под атрибутом Parchment.Attributor.
Attributor
и три функциональных классаclass
,style
,store
- Зарегистрируйте экземпляр с помощью Quill.register()
import Parchment from 'parchment';
let config = {
scope: Parchment.Scope.INLINE,
whitelist: ['serif', 'monospace']
};
let FontClass = new Parchment.Attributor.Class('font', 'ql-font', config);
export { FontClass };
- Сначала создайте экземпляр конструктора Parchment.Attributor.Class, показанного ранее.
src/attributor/class.ts
Чтобы создать экземпляр и изменить класс, нужно выполнить конструктор конструктора.
class ClassAttributor extends Attributor {}
- Поскольку класс класса наследуется от Attributor, см.
src/attributor/attributor.ts
export default class Attributor {
constructor(attrName: string, keyName: string, options: AttributorOptions = {}) {}
}
Итак, мы выполняемlet FontClass = new Parchment.Attributor.Class('font', 'ql-font', config);
Когда вы хотите передать эти три параметра в конструктор.
3. Что делает эта строка кода
constructor(attrName: string, keyName: string, options: AttributorOptions = {}) {
// 记录属性名和类名
this.attrName = attrName;
this.keyName = keyName;
let attributeBit = Registry.Scope.TYPE & Registry.Scope.ATTRIBUTE;
// 判断是否定义 scope 属性
if (options.scope != null) {
// 由于它是 scope 默认是通过二进制来表示的,所以,这里采用位运算来判断
this.scope = (options.scope & Registry.Scope.LEVEL) | attributeBit;
} else {
// 如果没有设置scope,则默认使用 ATTRIBUTE 二进制
this.scope = Registry.Scope.ATTRIBUTE;
}
if (options.whitelist != null) this.whitelist = options.whitelist;
}
Вы можете просмотреть путь к исходному коду реестра.tssrc/registry.ts
Необязательные значения, определяющие область действия, следующие: область действия определяет, является ли тип блота встроенным или блочным элементом.О том, какие операции являются встроенными, блочными, встраивания могут щелкнуть здесь, чтобы просмотреть
export enum Scope {
TYPE = (1 << 2) - 1, // 0011 Lower two bits
LEVEL = ((1 << 2) - 1) << 2, // 1100 Higher two bits
ATTRIBUTE = (1 << 0) | LEVEL, // 1101
BLOT = (1 << 1) | LEVEL, // 1110
INLINE = (1 << 2) | TYPE, // 0111
BLOCK = (1 << 3) | TYPE, // 1011
BLOCK_BLOT = BLOCK & BLOT, // 1010
INLINE_BLOT = INLINE & BLOT, // 0110
BLOCK_ATTRIBUTE = BLOCK & ATTRIBUTE, // 1001
INLINE_ATTRIBUTE = INLINE & ATTRIBUTE, // 0101
ANY = TYPE | LEVEL,
}
Quill другие методы API
Нажмите, чтобы просмотреть официальный документЭти методы могут бытьcore/quill.js
см. в документации, чтобы увидеть, как это реализовано
Пергаменты и дельты
Управление моделями документов и описание форматированного текстового содержимого в Quill основано на Parchment и Delta, соответственно, на обоих,Quill才能够通过 API 来操作富文本样式,定制化和扩展富文本功能。
Эти две функции следующие:
- Parchment использует Blots вместо dom для описания документов. Основная функция Parchment — управлять моделью документа. Мы можем инициализировать DOM через предоставляемый им интерфейс, возвращать указанный формат или указывать метку и область действия и так далее.
- Создавая экземпляр delta, мы перейдем в
参数配置项挂载到 ops
, и многие доступные методы смонтированы на этом прототипе экземпляра для работы с документом.
исходный код пергамента
основной файл
дорожка:src/Parchment.ts
,
let Parchment = {
Scope: Registry.Scope,
create: Registry.create,
register: Registry.register,
Container: ContainerBlot,
Format: FormatBlot,
Embed: EmbedBlot,
Scroll: ScrollBlot,
Block: BlockBlot,
Inline: InlineBlot,
Text: TextBlot,
Attributor: {
Attribute: Attributor,
Class: ClassAttributor,
Style: StyleAttributor,
Store: AttributorStore,
},
}
Просмотр дополнительных свойств параметра
модуль управления стилем
Папка атрибутов содержит некоторые методы для установки атрибутов узла. Путь:src/Parchment.ts
Объект, предоставляемый файлом, содержит все методы, предоставляемые пергаментом.
import Attributor from './attributor/attributor';
import ClassAttributor from './attributor/class';
import StyleAttributor from './attributor/style';
import AttributorStore from './attributor/store';
let Parchment = {
Attributor: {
Attribute: Attributor,
Class: ClassAttributor,
Style: StyleAttributor,
Store: AttributorStore,
},
}
В качестве примера стиля вы можете увидеть, как Attributor реализует управление стилями узлов.
class StyleAttributor extends Attributor {
add(node: HTMLElement, value: string): boolean {
if (!this.canAdd(node, value)) return false;
// @ts-ignore
node.style[camelize(this.keyName)] = value;
return true;
}
remove(node: HTMLElement): void {
// @ts-ignore
node.style[camelize(this.keyName)] = '';
if (!node.getAttribute('style')) {
node.removeAttribute('style');
}
}
value(node: HTMLElement): string {
// @ts-ignore
let value = node.style[camelize(this.keyName)];
return this.canAdd(node, value) ? value : '';
}
}
Способ реализации на самом деле очень прост, т. е. черезelement.style.color = '#f00'
Такие форматы стилизуют элемент.
-
class.ts
используется для установки класса -
store.ts
Формат, используемый для установки стиля записи атрибута, следующий: атрибуты записывают стиль. в следующей структуреattributes
Используется для управления изменениями свойств
{
ops: [
{ insert: 'Gandalf', attributes: { bold: true } },
{ insert: 'Grey', attributes: { color: '#cccccc' } }
]
}
-
attributor.ts
Эквивалентный базовому классу трех других классов, он определяет некоторые общедоступные свойства и методы.
export default class Attributor {
attrName: string;
keyName: string;
scope: Registry.Scope;
whitelist: string[] | undefined;
}
Модуль управления элементами
Путь:src/blot/abstract
Структура папки следующая
В этих файлах определены многие основные общие методы, такие какformat.ts
Некоторые методы форматирования определены в файле следующим образом
class FormatBlot extends ContainerBlot implements Formattable {
format(){}
formats(){}
replaceWith(){}
update(){}
wrap(){}
}
Затем в пути какsrc/blot/blot.ts
Введено использование файла format.ts, а повторное использование логики в FormatBlot реализовано через наследование.
import FormatBlot from './abstract/format';
class BlockBlot extends FormatBlot {
static blotName = 'block';
static scope = Registry.Scope.BLOCK_BLOT;
static tagName = 'P';
format(name: string, value: any) {
if (Registry.query(name, Registry.Scope.BLOCK) == null) {
return;
} else if (name === this.statics.blotName && !value) {
this.replaceWith(BlockBlot.blotName);
} else {
super.format(name, value);
}
}
}
Blots
Пергамент заменяет dom для описания документов. Кляксы эквивалентны элементам. Ниже приводится определение клякс.
// 拥有以下字段和静态方法,以及这些属性的类型
class Blot {
static blotName: string;
static className: string;
static tagName: string;
// inline or block
static scope: Scope;
domNode: Node;
prev: Blot;
next: Blot;
parent: Blot;
// Creates corresponding DOM node
static create(value?: any): Node;
// Apply format to blot. Should not pass onto child or other blot.
format(format: name, value: any);
insertAt(index: number, text: string);
// ...
}
Deltas
var delta = new Delta([
{ insert: 'Gandalf', attributes: { bold: true } }
]);
При создании Delta, элементы конфигурации параметров, которые мы передаем, будут установлены под OPS, и многие доступные методы установлены на этом прототипе этого экземпляра.
генерировать текст
var delta = new Delta([
{ insert: 'Gandalf', attributes: { bold: true } },
{ insert: ' the ' },
{ insert: 'Grey', attributes: { color: '#ccc' } }
]);
Дельта — это простой формат JSON, используемый для описания расширенного текстового содержимого. В приведенном выше примере показано, чтоGandalf the Grey
строка иGandalf 字样为 bold , Grey 的字体颜色为 #ccc
API изменить текст
Выше мы модифицируем документ через API, предоставленный Deltas:
var death = new Delta().retain(12).delete(4).insert('White', { color: '#fff' });
JSON, описывающее вышеуказанное поведение, заключается в следующем, Delta сохраняет первые 12 битов исходной строки, удаляет последние четыре бита на вершине этого и вставляетWhite
Стройный стиль шрифта белый
{
ops: [
{ retain: 12 },
{ delete: 4 },
{ insert: 'White', attributes: { color: '#fff' } }
]
}