地址Notes Canvas
背景
本人经常使用Notability看一下pdf书籍,这个app可以在pdf做笔记,十分巴适。但是有些文章并不一定产pdf资料,大多通过web预览,那么是否可以在web上划线记录笔记?便有这个想法。
思路
当时想法:
- 先把web转成pdf,再通过canvas涂写
- 直接在web加上一层canvas,直接涂写
考虑后面存在数据(目前存本地,后续将考虑存放服务),以及生成pdf可能存在弊端,就用了第二个想法。
准备
先找操作canvas第三库,轮子南造。
- fabric.js库,基本满足需求,画笔、箭头、文字以及擦除,简单的笔记操作足够了。
- jquery.js, 操作dom库,方便创建一下元素事件。
实现
操作图如下:
序号笔实现
序号笔,也是看了QQ截图有这个功能,尝试实现,它由一个方形,一个text组成的。
代码思路:
由Rect、Text、 Textbox组成一个group,当双击group时,把旧的Textbox隐藏,在相同位置创建一个可编辑可输入Textbox,当编辑结束,再把新的Textbox替换成旧的Textbox。
当页面存在数据重新展示笔记,画布初始化时,fabricjs不会自动注册group的事件。所以得通过createSerialByInitCache重新再画布上添加序号笔。
function drawSerial(canvas, mouseFrom, mouseTo, color, drawWidth, other = {}) {
//绘制圆形
const circle = new fabric.Rect({
width: 18,
height: 18,
originX: 'center',//调整中心点的X轴坐标
originY: 'center',//调整中心点的Y轴坐标
fill:'#1296db',
});
let textNum = 1
if (!other.num) {
g_fc_serial_num = g_fc_serial_num + 1
textNum = g_fc_serial_num
} else {
textNum = other.num
}
//绘制文本
const text = new fabric.Text(textNum + '' , {
fontSize: 8,
fill: '#fff',
originX: 'center',
originY: 'center'
})
const textbox = new fabric.Textbox( other.boxDesc ? other.boxDesc : "双击输入内容", {
left: 14,
top: -5,
fontSize: 18,
borderColor: other.borderColor ? other.borderColor : "#FF0000",
fill: other.fill ? other.fill : "#FF0000",
editingBorderColor: other.editingBorderColor ? other.editingBorderColor : "#FF0000"
});
//进行组合
const group = new fabric.Group([circle, text, textbox], {
left: mouseFrom.x,
top: mouseFrom.y,
hasControls: false,
drawType: 'serial',
})
// group.set('drawType', 'serial')
group.on("mousedblclick", function(event){
const tb = event.target.item(2)
// const mCanvas = canvas.viewportTransform;
// let mObject = tb.calcTransformMatrix();
// let mTotal = fabric.util.multiplyTransformMatrices(mCanvas, mObject);
// let trans = fabric.util.qrDecompose(mTotal);
// console.log(trans)
// 创建临时编辑文本对象
let tempText = new fabric.Textbox(tb.text, {
//... 旧文本对象需要克隆到临时的文本对象上,为了保证模拟出来的编辑框内容视觉统一。
left: tb.group.get('left') + 25,
top: tb.group.get('top') + 2,
fontSize: 18,
borderColor: tb.get('borderColor'),
fill: tb.get('fill'),
editingBorderColor: tb.get('editingBorderColor'),
});
//...
tempText.on("editing:exited", () => {
// 退出编辑态处理,
// 将 text value 赋值给原始文本对象 this.item(1)
// 将临时文本对象干掉
tb.group.addWithUpdate(tempText)
tb.group.remove(tb)
canvas.remove(tempText);
requestAnimationFrame(()=> {
g_fc_textbox = null
})
});
// textbox.enterEditing();
// textbox.hiddenTextarea.focus();
tb.set({
visible: false,
});
// 将临时文本对象加入画布,并激活,选中进入编辑态
canvas.add(tempText);
canvas.setActiveObject(tempText);
tempText.selectAll();
tempText.enterEditing();
requestAnimationFrame(()=> {
g_fc_textbox = tempText
})
});
return group;
}
function setSerialSortNum(canvas) {
const elDatas = canvas.getObjects()
let serialEls = elDatas.filter(item=> {
return item.get('drawType') === 'serial'
})
g_fc_serial_num = serialEls.length
serialEls = serialEls.sort((a, b)=> {
return Number(a.item(1).get('text')) - Number(b.item(1).get('text'))
})
serialEls.forEach((el, index)=> {
let text = el.item(1)
text.set('text', (index + 1) + '')
})
requestAnimationFrame(()=> {
canvas.renderAll();
})
}
function createSerialByInitCache(canvas) {
const elDatas = canvas.getObjects()
let serialEls = elDatas.filter(item=> {
return item.get('drawType') === 'serial'
})
g_fc_serial_num = serialEls.length
let gobj = null
serialEls.forEach((el)=> {
const text = el.item(1)
const tb = el.item(2)
gobj = drawSerial(canvas, { x: el.left, y: el.top}, null, null, null,
{
num: text.get('text'),
boxDesc: tb.get('text'),
borderColor: tb.get('borderColor'),
fill: tb.get('fill'),
editingBorderColor: tb.get('editingBorderColor'),
})
canvas.remove(el)
canvas.add(gobj)
})
}
其他
画笔、文字等官网都有相应的demo,可以照猫画虎。
存储
目前通过localStorage存储,页面地址当作key。
遇到的坑
Manifest v3 跟 Manifest v2
插件发布时,必须 Manifest v3 版本,不然没法通过,之前开发的时候就使用v2,于是就存在很多问题。 manifest_version密钥从更改2为3
//Manifest v2
"manifest_version": 2
//Manifest v3
"manifest_version": 3
web_accessible_resources 资源问题
报错: manifest v3 Resources must be listed in the web_accessible_resources manifest key in order to be loaded by pages outside the extension. 解决:(注意 matches 这个配置一定要加上,不然访问不了)
//Manifest v2
"web_accessible_resources": [
"js/options.js",
"js/main.js",
"js/injected.js"
]
//Manifest v3
"web_accessible_resources": [{
"resources": ["images/copy.svg"],
"matches": ["<all_urls>"],
"extension_ids": []
}],
访问资源:
//Manifest v2
chrome.extension.getURL(params.url)
//Manifest v3
chrome.runtime.getURL(params.url)
page_action 图标配置
//Manifest v2
"browser_action": {...}
"page_action": {...}
//Manifest v3
"action": {...}
background 背景页
//Manifest v2
"background": {
"scripts": ["js/background.js"]
}
//Manifest v3
"background": {
"service_worker": "js/background.js"
}
|