improve docs
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
# TextFrame
|
# TextFrame
|
||||||
一个通过canvas将长文本分成若干页渲染的js工具,处理排版禁则,支持辅助平面Unicode字符。目前仅工作于浏览器环境。
|
一个通过canvas将长文本分成若干页渲染的js工具,处理排版禁则,支持辅助平面Unicode字符。目前仅工作于浏览器环境。
|
||||||
|
|
||||||
|
[中文版](https://github.com/mattuylee/text-frame) | [English](https://github.com/mattuylee/text-frame/blob/master/docs/en/README-EN.md)
|
||||||
|
|
||||||
## 项目介绍
|
## 项目介绍
|
||||||
在浏览器中,尽管css有分栏布局,但却很难实现“分页”。浏览器没有提供计算一个盒子可以显示多少文本的API,只有Canvas的上下文提供了一个`measureText()`方法,但它只能简单的计算一段文本的渲染宽度,当文本不止一行时就无法计算了。由于不同字体不同字符有不同的宽度,再加上各浏览器对排版禁则的处理不一致,基本杜绝了计算浏览器原生DOM元素能够显示多少文本的可能。
|
在浏览器中,尽管css有分栏布局,但却很难实现“分页”。浏览器没有提供计算一个盒子可以显示多少文本的API,只有Canvas的上下文提供了一个`measureText()`方法,但它只能简单的计算一段文本的渲染宽度,当文本不止一行时就无法计算了。由于不同字体不同字符有不同的宽度,再加上各浏览器对排版禁则的处理不一致,基本杜绝了计算浏览器原生DOM元素能够显示多少文本的可能。
|
||||||
|
|
||||||
@@ -31,11 +33,11 @@
|
|||||||
| 破折号 | ── |
|
| 破折号 | ── |
|
||||||
| 省略号 | …… |
|
| 省略号 | …… |
|
||||||
|
|
||||||
本项目默认按照上述禁则处理排版禁则,但可以通过[选项](https://mattuylee.github.io/text-frame/zh/options.md)控制排版禁则。
|
本项目默认按照上述禁则处理排版禁则,但可以通过[选项](https://github.com/mattuylee/text-frame/blob/master/docs/zh/options.md)控制排版禁则。
|
||||||
草案中规定,当碰到行首为(行首禁则中的)标点时,应遵守「先挤进,后推出」原则,即先尝试压缩当前行的标点,无法挤压再取前一行的最后一个字至下一行。考虑到标点符号压缩的复杂性,本项目暂未实现该特性,而是直接尝试取上一行的最后一字到下一行,如果直到上一行首都没找到允许出现在行首的字符,则采取不处理的方式。
|
草案中规定,当碰到行首为(行首禁则中的)标点时,应遵守「先挤进,后推出」原则,即先尝试压缩当前行的标点,无法挤压再取前一行的最后一个字至下一行。考虑到标点符号压缩的复杂性,本项目暂未实现该特性,而是直接尝试取上一行的最后一字到下一行,如果直到上一行首都没找到允许出现在行首的字符,则采取不处理的方式。
|
||||||
|
|
||||||
## 功能
|
## 功能
|
||||||
本项目定义了文本片段(text fragment)的概念。通过在选项中指定一个文本片段数组,可以对每一个片段设置不同的样式,如字体、颜色、对齐方式等,具体请参考[配置项](https://mattuylee.github.io/text-frame/zh/options.md)。一般来说,一个文本片段即一个文本段落,虽然文本片段内也支持换行,但不会有段间距和行缩进。在本项目文档中文本片段和文本段落等价,都是指`FragmentOptions`定义的文本片段。
|
本项目定义了文本片段(text fragment)的概念。通过在选项中指定一个文本片段数组,可以对每一个片段设置不同的样式,如字体、颜色、对齐方式等,具体请参考[配置项](https://github.com/mattuylee/text-frame/blob/master/docs/zh/options.md)。一般来说,一个文本片段即一个文本段落,虽然文本片段内也支持换行,但不会有段间距和行缩进。在本项目文档中文本片段和文本段落等价,都是指`FragmentOptions`定义的文本片段。
|
||||||
|
|
||||||
本项目支持以下特性:
|
本项目支持以下特性:
|
||||||
* 每个段落单独设置字体、缩进、边距、字体颜色等;
|
* 每个段落单独设置字体、缩进、边距、字体颜色等;
|
||||||
@@ -109,7 +111,7 @@ renderFrame(canvas.getContext('2d'), frames[0]);
|
|||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
配置说明请参考[配置项](https://mattuylee.github.io/text-frame/zh/options.md)。
|
配置说明请参考[配置项](https://github.com/mattuylee/text-frame/blob/master/docs/zh/options.md)。
|
||||||
|
|
||||||
|
|
||||||
## 源码编译
|
## 源码编译
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# TextFrame
|
# TextFrame
|
||||||
|
|
||||||
[中文版](https://github.com/mattuylee/text-frame) | [English](https://github.com/mattuylee/text-frame/docs/en/README-EN.md)
|
[中文版](https://github.com/mattuylee/text-frame) | [English](https://github.com/mattuylee/text-frame/blob/master/docs/en/README-EN.md)
|
||||||
|
|
||||||
A javascript tool to split long text into frames, with typesetting prohibition processed and unicode full support.
|
A javascript tool to split long text into frames, with typesetting prohibition processed and unicode full support.
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ refering to [W3C draft](https://www.w3.org/TR/2020/WD-clreq-20201001/#prohibitio
|
|||||||
| Em dash and long dash | ── |
|
| Em dash and long dash | ── |
|
||||||
| Ellipsis | …… |
|
| Ellipsis | …… |
|
||||||
|
|
||||||
This library take the above as default rules, but it can be configued with [options](https://mattuylee.github.io/text-frame/en/options.md).
|
This library take the above as default rules, but it can be configued with [options](https://github.com/mattuylee/text-frame/blob/master/docs/en/options.md).
|
||||||
|
|
||||||
Here is an online [demo](https://mattuylee.github.io/text-frame/en/example.html).
|
Here is an online [demo](https://mattuylee.github.io/text-frame/en/example.html).
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ renderFrame(canvas.getContext('2d'), frames[0], true);
|
|||||||
// ...render
|
// ...render
|
||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
Option reference sits [here](https://mattuylee.github.io/text-frame/en/options.md).
|
Option reference sits [here](https://github.com/mattuylee/text-frame/blob/master/docs/en/options.md).
|
||||||
|
|
||||||
|
|
||||||
## Build from Source
|
## Build from Source
|
||||||
|
|||||||
@@ -0,0 +1,138 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Test</title>
|
||||||
|
<style>
|
||||||
|
.example-container {
|
||||||
|
display: flex;
|
||||||
|
min-width: 360px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
max-width: 1024px;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-item {
|
||||||
|
box-sizing: border-box;
|
||||||
|
min-width: 300px;
|
||||||
|
width: 300px;
|
||||||
|
margin: 16px;
|
||||||
|
}
|
||||||
|
.example-item:first-child {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
#source {
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 16px;
|
||||||
|
height: 360px;
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#canvas {
|
||||||
|
width: 100%;
|
||||||
|
height: 360px;
|
||||||
|
border: 1px solid darkgray;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script src="../../dist/umd/text-frame.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="example-container">
|
||||||
|
<div class="example-item">
|
||||||
|
<textarea id="source" oninput="refresh()" spellcheck="false">
|
||||||
|
options = {
|
||||||
|
viewWidth: 300,
|
||||||
|
viewHeight: 360,
|
||||||
|
fontSize: 16,
|
||||||
|
margin: 16,
|
||||||
|
fragments: [
|
||||||
|
{
|
||||||
|
color: 'green',
|
||||||
|
fontSize: 20,
|
||||||
|
margin: 32,
|
||||||
|
textAlign: 'center',
|
||||||
|
text: "Caption"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fontFamily: 'serif',
|
||||||
|
margin: { bottom: 32 },
|
||||||
|
marginCollapse: true,
|
||||||
|
textAlign: 'justify',
|
||||||
|
text: "This is a multi-line text."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
</textarea>
|
||||||
|
<a href="https://github.com/mattuylee/text-frame/blob/master/docs/en/options.md"><button>Option Reference</button></a>
|
||||||
|
</div>
|
||||||
|
<div class="example-item">
|
||||||
|
<canvas id="canvas"></canvas>
|
||||||
|
<button onclick="setPagination(-1)">Previous</button>
|
||||||
|
<button onclick="setPagination(1)">Next</button>
|
||||||
|
<span id="pagination" style="float: right;">0 / 0</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
var options
|
||||||
|
, pagination = document.getElementById('pagination')
|
||||||
|
, canvas = document.getElementById('canvas')
|
||||||
|
, ctx = canvas.getContext('2d')
|
||||||
|
, pageIndex = 0
|
||||||
|
, frames = []
|
||||||
|
, defaultOptions = {
|
||||||
|
viewWidth: 300,
|
||||||
|
viewHeight: 360,
|
||||||
|
fontWeight: '700',
|
||||||
|
fontSize: 20,
|
||||||
|
color: 'red',
|
||||||
|
fragments: [
|
||||||
|
{
|
||||||
|
textAlign: 'center',
|
||||||
|
margin: 16,
|
||||||
|
marginCollapse: false,
|
||||||
|
text: "bad input"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
canvas.width = 300 * devicePixelRatio;
|
||||||
|
canvas.height = 360 * devicePixelRatio;
|
||||||
|
window.onresize = refresh;
|
||||||
|
refresh();
|
||||||
|
|
||||||
|
function refresh() {
|
||||||
|
try {
|
||||||
|
var src = document.getElementById('source').value;
|
||||||
|
new Function(src)();
|
||||||
|
frames = TextFrame.computeTextFrames(options);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
frames = TextFrame.computeTextFrames(defaultOptions);
|
||||||
|
}
|
||||||
|
pageIndex = 0;
|
||||||
|
setPagination(0)
|
||||||
|
}
|
||||||
|
function setPagination(delta) {
|
||||||
|
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||||
|
if (!frames.length) {
|
||||||
|
pagination.textContent = '0 / 0';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pageIndex += delta;
|
||||||
|
if (pageIndex < 0) { pageIndex = 0; }
|
||||||
|
else if (pageIndex >= frames.length) {
|
||||||
|
pageIndex = frames.length - 1;
|
||||||
|
}
|
||||||
|
TextFrame.renderFrame(ctx, frames[pageIndex]);
|
||||||
|
pagination.textContent = pageIndex + 1 + ' / ' + frames.length;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
+1
-1
@@ -24,7 +24,7 @@ export function renderFrame(context: CanvasRenderingContext2D, frame: TextFrame,
|
|||||||
|
|
||||||
### canvasHeight
|
### canvasHeight
|
||||||
* Type: `number`
|
* Type: `number`
|
||||||
* Default: 根据`viewHeight`计算。
|
* Default: compute with `viewHeight`
|
||||||
* Description: canvas.height is `viewHeight * window.devicePixelRatio`
|
* Description: canvas.height is `viewHeight * window.devicePixelRatio`
|
||||||
|
|
||||||
### margin
|
### margin
|
||||||
|
|||||||
@@ -0,0 +1,139 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Test</title>
|
||||||
|
<style>
|
||||||
|
.example-container {
|
||||||
|
display: flex;
|
||||||
|
min-width: 360px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
max-width: 1024px;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-item {
|
||||||
|
box-sizing: border-box;
|
||||||
|
min-width: 300px;
|
||||||
|
width: 300px;
|
||||||
|
margin: 16px;
|
||||||
|
}
|
||||||
|
.example-item:first-child {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
#source {
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 16px;
|
||||||
|
height: 360px;
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#canvas {
|
||||||
|
width: 100%;
|
||||||
|
height: 360px;
|
||||||
|
border: 1px solid darkgray;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script src="../../dist/umd/text-frame.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="example-container">
|
||||||
|
<div class="example-item">
|
||||||
|
<textarea id="source" oninput="refresh()" spellcheck="false">
|
||||||
|
options = {
|
||||||
|
viewWidth: 300,
|
||||||
|
viewHeight: 360,
|
||||||
|
fontSize: 16,
|
||||||
|
margin: 16,
|
||||||
|
fragments: [
|
||||||
|
{
|
||||||
|
color: 'green',
|
||||||
|
fontSize: 20,
|
||||||
|
margin: 32,
|
||||||
|
textAlign: 'center',
|
||||||
|
text: "标题"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
textIndent: 32,
|
||||||
|
fontFamily: 'serif',
|
||||||
|
margin: { bottom: 32 },
|
||||||
|
marginCollapse: true,
|
||||||
|
textAlign: 'justify',
|
||||||
|
text: "这是一个多行文本。"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
</textarea>
|
||||||
|
<a href="https://github.com/mattuylee/text-frame/blob/master/docs/zh/options.md"><button>配置项</button></a>
|
||||||
|
</div>
|
||||||
|
<div class="example-item">
|
||||||
|
<canvas id="canvas"></canvas>
|
||||||
|
<button onclick="setPagination(-1)">上一页</button>
|
||||||
|
<button onclick="setPagination(1)">下一页</button>
|
||||||
|
<span id="pagination" style="float: right;">0 / 0</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
var options
|
||||||
|
, pagination = document.getElementById('pagination')
|
||||||
|
, canvas = document.getElementById('canvas')
|
||||||
|
, ctx = canvas.getContext('2d')
|
||||||
|
, pageIndex = 0
|
||||||
|
, frames = []
|
||||||
|
, defaultOptions = {
|
||||||
|
viewWidth: 300,
|
||||||
|
viewHeight: 360,
|
||||||
|
fontWeight: '700',
|
||||||
|
fontSize: 20,
|
||||||
|
color: 'red',
|
||||||
|
fragments: [
|
||||||
|
{
|
||||||
|
textAlign: 'center',
|
||||||
|
margin: 16,
|
||||||
|
marginCollapse: false,
|
||||||
|
text: "bad input"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
canvas.width = 300 * devicePixelRatio;
|
||||||
|
canvas.height = 360 * devicePixelRatio;
|
||||||
|
window.onresize = refresh;
|
||||||
|
refresh();
|
||||||
|
|
||||||
|
function refresh() {
|
||||||
|
try {
|
||||||
|
var src = document.getElementById('source').value;
|
||||||
|
new Function(src)();
|
||||||
|
frames = TextFrame.computeTextFrames(options);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
frames = TextFrame.computeTextFrames(defaultOptions);
|
||||||
|
}
|
||||||
|
pageIndex = 0;
|
||||||
|
setPagination(0)
|
||||||
|
}
|
||||||
|
function setPagination(delta) {
|
||||||
|
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||||
|
if (!frames.length) {
|
||||||
|
pagination.textContent = '0 / 0';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pageIndex += delta;
|
||||||
|
if (pageIndex < 0) { pageIndex = 0; }
|
||||||
|
else if (pageIndex >= frames.length) {
|
||||||
|
pageIndex = frames.length - 1;
|
||||||
|
}
|
||||||
|
TextFrame.renderFrame(ctx, frames[pageIndex]);
|
||||||
|
pagination.textContent = pageIndex + 1 + ' / ' + frames.length;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
+16
-7
@@ -38,7 +38,7 @@
|
|||||||
border: 1px solid darkgray;
|
border: 1px solid darkgray;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<script src="../dist/umd/text-frame.js"></script>
|
<script src="../../dist/umd/text-frame.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@@ -51,15 +51,24 @@
|
|||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
margin: 16,
|
margin: 16,
|
||||||
fragments: [
|
fragments: [
|
||||||
{
|
{
|
||||||
margin: { left: 16, right: 16, top: 32 },
|
color: 'green',
|
||||||
marginCollapse: false,
|
fontSize: 20,
|
||||||
text: "在这里输入文本。"
|
margin: 32,
|
||||||
},
|
textAlign: 'center',
|
||||||
|
text: "Caption"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fontFamily: 'serif',
|
||||||
|
margin: { bottom: 32 },
|
||||||
|
marginCollapse: true,
|
||||||
|
textAlign: 'justify',
|
||||||
|
text: "This is a multi-line text."
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
</textarea>
|
</textarea>
|
||||||
<button>Options Refrence</button>
|
<a href="https://github.com/mattuylee/text-frame/blob/master/docs/en/options.md"><button>Option Reference</button></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="example-item">
|
<div class="example-item">
|
||||||
<canvas id="canvas"></canvas>
|
<canvas id="canvas"></canvas>
|
||||||
|
|||||||
Generated
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "text-frame",
|
"name": "@mattuy/text-frame",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
|
|||||||
+1
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@mattuy/text-frame",
|
"name": "@mattuy/text-frame",
|
||||||
"version": "0.0.1",
|
"version": "0.0.4",
|
||||||
"private": false,
|
"private": false,
|
||||||
"description": "a javascript tool to split long text into pages, with typesetting prohibition processed and unicode full support.\n一个将长文本分成若干页的js工具,处理排版禁则,支持Unicode",
|
"description": "a javascript tool to split long text into pages, with typesetting prohibition processed and unicode full support.\n一个将长文本分成若干页的js工具,处理排版禁则,支持Unicode",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
@@ -11,7 +11,6 @@
|
|||||||
"build": "rollup -c rollup.config.js --environment NODE_ENV:development",
|
"build": "rollup -c rollup.config.js --environment NODE_ENV:development",
|
||||||
"dist": "rollup -c rollup.config.js --environment NODE_ENV:production",
|
"dist": "rollup -c rollup.config.js --environment NODE_ENV:production",
|
||||||
"debug": "rollup -c rollup.config.js --environment NODE_ENV:development --watch",
|
"debug": "rollup -c rollup.config.js --environment NODE_ENV:development --watch",
|
||||||
"install": "npm run build && npm run dist",
|
|
||||||
"example": "rollup -c rollup.config.js --environment NODE_ENV:development --environment SERVE"
|
"example": "rollup -c rollup.config.js --environment NODE_ENV:development --environment SERVE"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
Reference in New Issue
Block a user