原图
效果图
代码
index.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Fabric.js 加粗、下划线、斜体、竖排、字体对齐</title>
<script src="../fabric5.2.1.js"></script>
<style>
div#container {
padding: 30px;
font-family: 'verdana', lucida;
}
input {
background-color: #ccc;
padding: 0;
width: 300px;
color: #777;
}
a {
color: #777;
display: block;
background-color: #ccc;
width: 300px;
padding: 0;
margin-top: 2px;
text-decoration: none;
}
</style>
</head>
<body>
<div id="container">
<p>
<input type="color" id="color" name="color" onchange="changeColor(value)"/>
</p>
<p>
<button onclick="bold()">加粗</button>
<button onclick="overline()">上划线</button>
<button onclick="linethrough()">中划线</button>
<button onclick="underline()">下划线</button>
<button onclick="italic()">斜体</button>
<button onclick="textalign('left')">字体左对齐</button>
<button onclick="textalign('right')">字体右对齐</button>
<button onclick="textalign('center')">字体居中对齐</button>
</p>
<canvas id="imageCanvas" width="300" height="300"></canvas>
<a id="lnkDownload" href="#">点我保存图片</a>
</div>
<script src="script.js"></script>
</body>
</html>
script.js 代码
var canvas = new fabric.Canvas('imageCanvas', {
backgroundColor: 'rgb(240,240,240)',
includeDefaultValues: false,// 指示toObject/toDatalessObject是否应该包含默认值,如果设置为false,则优先于对象值
perPixelTargetFind: true, //这一句说明选中的时候以图形的实际大小来选择而不是以边框来选择
hasBorders: false,
});
canvas.setWidth(500);
canvas.setHeight(800);
// 使用 IText,可编辑文本
var text_1 = new fabric.IText(
'《天净沙·秋思》\n枯藤老树昏鸦,\n小桥流水人家,\n古道西风瘦马。\n夕阳西下,\n断肠人在天涯。',
{
width: 300,
fontSize: 14,
fontFamily: 'Comic Sans',
left: 50,
top: 100,
fill: 'blue',
splitByGrapheme: true, // 自动换行
}
);
canvas.add(text_1);
// 使用 IText,可编辑文本
var text_2 = new fabric.Textbox(
'《奇葩天地网》www.qipa250.com',
{
width: 300, // 建议设置 textbox 的宽度
fontSize: 14,
fontFamily: 'Comic Sans',
left: 50,
top: 400,
fill: 'red',
}
);
//禁止用户调整文本框高度
/*
setControlVisible 第一个参数是操作点(控件)的键,分别有 tl, tr, br, bl, ml, mt, mr, mb, mtr
mt 表示中间顶部的操作点,mb 表示中间底部的操作点。
第二个参数设置控件是否可见。true 是默认值,表示可见;设置成 false 代表指定控件不可见。
*/
//禁止下拉,防止字体变形
text_2.setControlVisible('mt', false);
text_2.setControlVisible('mb', false);
// splitByGrapheme 为 true 时,Textbox 里的文本就会实时根据宽度进行换行。
text_2.splitByGrapheme = true;
canvas.add(text_2);
function changeColor(value) {
console.log('changeColor--value=', value);
let activeTxt = canvas.getActiveObject();
if (!activeTxt) return;
if (activeTxt.isEditing) {
console.log('选中,修改单个文字的颜色');
// 编辑状态
activeTxt.setSelectionStyles({'fill': value});
} else {
console.log('选中整体,修改所有文字的颜色');
activeTxt.fill = value;
let s = activeTxt.styles;
for (let i in s) {
for (let j in s[i]) {
s[i][j].fill = value;
}
}
}
canvas.renderAll();
}
// 加粗
function bold() {
let activeTxt = canvas.getActiveObject();
if (!activeTxt) return;
if (activeTxt.isEditing) {
// 编辑状态
const state = activeTxt.getSelectionStyles().find(item => item.fontWeight !== 'bold');
if (!state || (JSON.stringify(state) === '{}' && activeTxt['fontWeight'] === 'bold')) {
activeTxt.setSelectionStyles({'fontWeight': 'normal'});
} else {
activeTxt.setSelectionStyles({'fontWeight': 'bold'});
}
} else {
// 选择状态
if (activeTxt['fontWeight'] === 'bold') {
activeTxt.fontWeight = 'normal';
let s = activeTxt.styles;
for (let i in s) {
for (let j in s[i]) {
s[i][j].fontWeight = 'normal';
}
}
} else {
activeTxt.fontWeight = 'bold';
let s = activeTxt.styles;
for (let i in s) {
for (let j in s[i]) {
s[i][j].fontWeight = 'bold';
}
}
}
}
canvas.renderAll();
}
//上划线
function overline() {
let activeTxt = canvas.getActiveObject(); // 获取当前选中的文字
// 如果当前没选中文字,那什么都不操作
if (!activeTxt) return;
// 判断当前是否进入编辑状态
if (activeTxt.isEditing) {
// 编辑状态
const state = activeTxt.getSelectionStyles().find(item => item.overline !== true);
// 如果当前
if (!state || (JSON.stringify(state) === '{}' && activeTxt['overline'] === true)) {
// 如果当前已经设置了上划线,那就把全局上划线取消
activeTxt.setSelectionStyles({'overline': false});
} else {
// 如果当前没设置上划线,那就添加上上划线
activeTxt.setSelectionStyles({'overline': true});
}
} else {
// 选择状态
if (activeTxt['overline'] === true) {
activeTxt.overline = false;
activeTxt.dirty = true;
let s = activeTxt.styles;
for (let i in s) {
for (let j in s[i]) {
s[i][j].overline = false;
}
}
} else {
activeTxt.overline = true;
activeTxt.dirty = true;
let s = activeTxt.styles;
for (let i in s) {
for (let j in s[i]) {
s[i][j].overline = true;
}
}
}
}
canvas.renderAll();
}
//中划线
function linethrough() {
//获取当前选中的文字
let activeTxt = canvas.getActiveObject();
// 如果当前没选中文字,那什么都不操作
if (!activeTxt) return;
// 判断当前是否进入编辑状态
if (activeTxt.isEditing) {
// 编辑状态
const state = activeTxt.getSelectionStyles().find(item => item.linethrough !== true);
// 如果当前
if (!state || (JSON.stringify(state) === '{}' && activeTxt['linethrough'] === true)) {
// 如果当前已经设置了中划线,那就把全局中划线取消
activeTxt.setSelectionStyles({'linethrough': false});
} else {
// 如果当前没设置中划线,那就添加上中划线
activeTxt.setSelectionStyles({'linethrough': true});
}
} else {
// 选择状态
if (activeTxt['linethrough'] === true) {
activeTxt.linethrough = false;
activeTxt.dirty = true;
let s = activeTxt.styles;
for (let i in s) {
for (let j in s[i]) {
s[i][j].linethrough = false;
}
}
} else {
activeTxt.linethrough = true;
activeTxt.dirty = true;
let s = activeTxt.styles;
for (let i in s) {
for (let j in s[i]) {
s[i][j].linethrough = true;
}
}
}
}
canvas.renderAll();
}
//下划线
function underline() {
//获取当前选中的文字
let activeTxt = canvas.getActiveObject();
// 如果当前没选中文字,那什么都不操作
if (!activeTxt) return;
// 判断当前是否进入编辑状态
if (activeTxt.isEditing) {
// 编辑状态
const state = activeTxt.getSelectionStyles().find(item => item.underline !== true);
// 如果当前
if (!state || (JSON.stringify(state) === '{}' && activeTxt['underline'] === true)) {
// 如果当前已经设置了下划线,那就把全局下划线取消
activeTxt.setSelectionStyles({'underline': false});
} else {
// 如果当前没设置下划线,那就添加上下划线
activeTxt.setSelectionStyles({'underline': true});
}
} else {
// 选择状态
if (activeTxt['underline'] === true) {
activeTxt.underline = false;
activeTxt.dirty = true;
let s = activeTxt.styles;
for (let i in s) {
for (let j in s[i]) {
s[i][j].underline = false;
}
}
} else {
activeTxt.underline = true;
activeTxt.dirty = true;
let s = activeTxt.styles;
for (let i in s) {
for (let j in s[i]) {
s[i][j].underline = true;
}
}
}
}
canvas.renderAll();
}
//斜体
function italic() {
let activeTxt = canvas.getActiveObject();
if (!activeTxt) return;
if (activeTxt.isEditing) {
// 编辑状态
const state = activeTxt.getSelectionStyles().find(item => item.fontStyle !== 'italic');
if (!state || (JSON.stringify(state) === '{}' && activeTxt['fontStyle'] === 'italic')) {
activeTxt.setSelectionStyles({'fontStyle': 'normal'});
} else {
activeTxt.setSelectionStyles({'fontStyle': 'italic'});
}
} else {
// 选择状态
if (activeTxt['fontStyle'] === 'italic') {
activeTxt.fontStyle = 'normal';
let s = activeTxt.styles;
for (let i in s) {
for (let j in s[i]) {
s[i][j].fontStyle = 'normal';
}
}
} else {
activeTxt.fontStyle = 'italic';
let s = activeTxt.styles;
for (let i in s) {
for (let j in s[i]) {
s[i][j].fontStyle = 'italic';
}
}
}
}
canvas.renderAll();
}
//文字对齐
function textalign(align_value) {
canvas.getActiveObject().textAlign = align_value;
canvas.renderAll();
}
var imageSaver = document.getElementById('lnkDownload');
imageSaver.addEventListener('click', saveImage, false);
function saveImage(e) {
console.log('toJSON==', canvas.toJSON());
console.log('toObject==', canvas.toObject()); // 输出序列化的内容
this.href = canvas.toDataURL({
format: 'png',
quality: 0.8
});
this.download = 'canvas.png';
}