Html5图像标记

分享于 

18分钟阅读

Web开发

  繁體

下载ImageMarkup源 1.5 MB

Image Markup

图 1.工作中的图像标记插件,显示图形和文字特征。

介绍

我需要从internet上下载一个图像,打开一些图像编辑应用程序,添加一些标记,然后使用它。 如果网站本身允许用户注释图像在线( 使用文本和图形),然后将组合图像保存到网络上某些地方。 本文介绍了实现这种特性的jQuery插件,至少以简化的方式实现。

Background

这种直接注释被认为是学习管理系统( ILang )的一个好资产,教师可以检查图像扫描开放结束答案。 这样的视觉注释可以保持到数据库的评估和级别上,以表示开放结束问题评估的整个过程。

系统相关性

应用程序将在客户端浏览器上驻留 100%。 它是用JavaScript编写的,依赖四个主要框架/库:

使用代码

作为任何好的jQuery插件,图像标记只需要一行JavaScript初始化代码,那就是构建 CANVAS 注释层的IMAGE 元素。 例如:

var markup = $('#myImage').imageMarkup();

上面 行将创建附加到 IMAGE的图像标记实例,该标记的id等于 myImage。

用户可能会发现将图像标记实例附加到多个 IMAGE 元素有帮助。 在下面的例子中,将向每个 IMAGE 元素 inside 附加一个图像标记实例,该元素具有"img容器"类:

var markup = $('.img-container img').imageMarkup();

替代默认选项

可以覆盖一些属性,以定制插件的工作方式。 它们是:

  • 颜色:图形和文本元素的颜色。 这可以在你开始工作后改变。
  • 宽度:你可以预定义绘图的厚度。 显然,这只适用于绘图,而不适用于文本元素。
  • 不透明度:因为图形和文本上的图像可能会令人混淆,有时添加一些透明度是很有用的。 因此,你可以选择从 0.0到 1.0的不透明度范围。

below 指定带有红色笔的图像标记实例。4点的厚度和 50%的不透明度:

var markup = $('.img-container img').imageMarkup({color: 'red', width: 10, opacity:. 5});

都是关于层

注释永远不应该修改底层图像。 不是因为我们想保留原始图像,而是要将注释保存为实体,以后将它的保存到 Web服务 存储。

单独的注释层还具有其他优点: 在java对象结构中封装元素集之后,可以轻松地将它的序列化为JSON格式,并将注释持久化到数据库对象中的字符串列。 相反,稍后你可以检索相同的JSON字符串并恢复注释。

图 2.透明画布下带有注释( B )的图像层( A ) 生成了一个新的合成图像( C )。

图像标记插件创建了一个新的HTML5 Canvas 元素,它覆盖整个图像。 这里画布作为图像元素的同级添加,因此它们共享相同的祖先。

通过图像绘制

插件的功能是徒手绘制: 拖动鼠标在画布上留下一个直线段,一旦用户释放鼠标按钮,就会变成smoothened剪辑。 用户可以绘制无限数量的点和独立行。

当用户单击鼠标按钮时开始。 此时,创建 Path 对象的新实例,并将默认设置应用于路径( 颜色,宽度和不透明度):

tool.onMouseDown = function (event) {
 switch (event.event.button) {
 // leftclickcase0:
 // If we produced a path before, deselect it:if (path) {
 path.selected = false;
 }
 path = new paper.Path();
 path.data.id = generateUUID();
 path.strokeColor = settings.color;
 path.strokeWidth = settings.width;
 path.opacity = settings.opacity;
 break;
 // rightclickcase2:
 break;
 }
} 

below,通过实现图书馆对象的图表 JS toolonMouseDrag 事件来检测和处理拖动效果: 当用户拖动鼠标时,新的点被添加到当前 Path 对象。

tool.onMouseDrag = function (event) {
 switch (event.event.button) {
 // leftclickcase0:
 // Every drag event, add a point to the path at the current// position of the mouse:if (selectedItem) {
. 
 [DRAG (MOVE) THE SELECTED ITEM]
. 
 }
 elseif (path)
 path.add(event.point);
 break;
 // rightclickcase2:
 break;
 }
}

现在订阅 onMouseUp 事件,当用户释放鼠标按钮时,路径将被简化为( 即,线段之间的直线是 smoothened )。

tool.onMouseUp = function (event) {
 switch (event.event.button) {
 // leftclickcase0:
 if (selectedItem) {
. 
 [STOP DRAGGING THE SELECTED ITEM]
. 
 }
 else {
 // When the mouse is released, simplify it: path.simplify();
. 
 [SAVE THE PATH COMMAND IN COMMAND MANAGER]
. 
 }
 break;
 // rightclick. 
. 
. 
 }
} 

每个或者"涂鸦"只由几个点组成,而不是像直线那样显示,它们之间的段是类似于手写体的smoothened。

用户可以更改钢笔颜色,但从有限的设置中选择: black,红色,绿色和 yellow。

this.setPenColor = function (color) {
 self.setOptions({ color: color });
 $('.image-markup-canvas').css('cursor', "url(img/" + color + "-pen.png) 14 50, auto");
}

如前所述,透明度和厚度属性是在JavaScript初始化代码中定义的。 用户无法更改它们。

文字上方的文字

这里工具以"。创建,修改后修改"方式工作。 用户可以通过单击上下文菜单中的文本工具添加文本注释,并且将在画布上删除带有默认消息的新文本元素。 通过双击元素并在浏览器对话框的输入中键入新文本,可以编辑文本。 文本颜色与绘图笔的颜色相同。

注意,如何通过 setText 函数在 ContextMenu 中设置文本工具:

$.contextMenu({
 selector: '.image-markup-canvas',
 callback: function (key, options) {
 switch (key) {
. 
. 
. 
 case'text':
 self.setText();
 break;
. 
. 
. 
 }
 },
 items: {
. 
. 
. 
 "text": { name: "Text", icon: "text" },
. 
. 
. 
 }
});

默认设置应用于 PointText 对象的新实例,onDoubleClick 函数准备浏览器输入对话框的默认文本:

this.setText = function () {
 var uid = generateUUID();
 var pos = contextPoint;
 CommandManager.execute({
 execute: function () {
 var TXT_DBL_CLICK = "<<double click to edit>>";
 var txt = TXT_DBL_CLICK;
 var text = new paper.PointText(pos);
 text.content = txt;
 text.fillColor = settings.color;
 text.fontSize = 18;
 text.fontFamily = 'Verdana';
 text.data.uid = uid;
 text.opacity = settings.opacity;
 text.onDoubleClick = function (event) {
 if (this.className == 'PointText') {
 var txt = prompt("Type in your text", this.content.replace(TXT_DBL_CLICK, ''));
 if (txt.length> 0)
 this.content = txt;
 }
 }
 },
 unexecute: function () {
 $(paper.project.activeLayer.children).each(function (index, item) {
 if (item.data && item.data.uid) {
 if (item.data.uid == uid) {
 item.remove();
 }
 }
 });
 }
 });
}

图 4.通过选择文本菜单项,然后双击文本,用户可以在图像上放置文本注释。

选择项

通过将鼠标移动到项目上,可以选择该项: 一系列的段处理程序将指示哪个项目已经被选中。

图 5.显示所选元素的路径段。

当用户将鼠标( 不拖动) 移动到元素( 路径或者文本) 时,库对象的图纸 JS toolonMouseMove 函数将元素转换为选定状态。

tool.onMouseMove = function (event) {
 if (!$('.context-menu-list').is(':visible')) {
 position = event.point;
 paper.project.activeLayer.selected = false;
 self.setPenColor(settings.color);
 if (event.item) {
 event.item.selected = true;
 selectedItem = event.item;
 self.setCursorHandOpen();
 }
 else {
 selectedItem = null;
 }
 }
}

当前,图像标记插件不支持多个选择。 但是,我打算在将来的版本中实现这里功能。

删除项目

注释元素很容易被菜单删除。 如果存在选定的项,则清除菜单将会 delete。 否则,它将 delete 所有的画布元素。

在画布元素的Collection 中搜索所选元素,然后在找到后删除。 注意操作是如何通过 CommandManager 对象完成的,以便在以后的用户请求时可以撤消它。

this.erase = function () {
 var strPathArray = new Array();
 $(paper.project.activeLayer.children).each(function (index, item) {
 if (contextSelectedItemId) {
 if (contextSelectedItemId.length == 0 || item.data.id == contextSelectedItemId) {
 var strPath = item.exportJSON({ asString: true });
 strPathArray.push(strPath);
 }
 }
 });
 CommandManager.execute({
 execute: function () {
 $(paper.project.activeLayer.children).each(function (index, item) {
 if (contextSelectedItemId) {
 if (contextSelectedItemId.length == 0 || item.data.id == contextSelectedItemId) {
 item.remove();
 }
 }
 });
 },
 unexecute: function () {
 $(strPathArray).each(function (index, strPath) {
 path = new paper.Path();
 path.importJSON(strPath);
 });
 }
 });
}

图 6.清除选定的注释项。

移动项目

你可以通过drag-and-drop在整个画布上移动单个元素( 绘图或者文本)。 但是,当前版本不允许同时移动多个元素。

当用户释放鼠标按钮时,选定的元素被放置在它的final 位置,CommandManager 被告知记录该操作。

tool.onMouseUp = function (event) {
 switch (event.event.button) {
 // leftclickcase0:
 if (selectedItem) {
 if (mouseDownPoint) {
 var selectedItemId = selectedItem.id;
 var draggingStartPoint = { x: mouseDownPoint.x, y: mouseDownPoint.y };
 CommandManager.execute({
 execute: function () {
 //item was already moved, so do nothing },
 unexecute: function () {
 $(paper.project.activeLayer.children).each(function (index, item) {
 if (item.id == selectedItemId) {
 if (item.segments) {
 var middlePoint = new paper.Point(
 ((item.segments[item.segments.length - 1].point.x) - item.segments[0].point.x)/2,
 ((item.segments[item.segments.length - 1].point.y) - item.segments[0].point.y)/2 );
 item.position =
 new paper.Point(draggingStartPoint.x, draggingStartPoint.y);
 }
 else {
 item.position = draggingStartPoint;
 }
 returnfalse;
 }
 });
 }
 });
 mouseDownPoint = null;
. 
. 
. 

下载合并图像

你可以通过单击上下文菜单中的Download菜单item将组合图像( 也就是说,源图像加上图形和文本注释) 下载为单个图像。 图像将直接转到浏览器的下载文件夹( 或者你指定为默认文件夹的文件夹)。

$.contextMenu({
 selector: '.image-markup-canvas',
 callback: function (key, options) {
 switch (key) {
 //COMMANDS. 
. 
. 
 case'download':
 self.download();
 break;
. 
. 
. 
 }
 },
 items: {
. 
. 
. 
 "download": { name: "Download", icon: "download" },
. 
. 
. 
 }
});

在代码 below 中,mergedContext 被创建为 CANVAS HTML元素( 虽然未附加到HTML页面,因此不可见)的新实例。 然后,调用 drawImage 方法两次: 一次绘制基础图像,再一次绘制包含在Paper库的帮助下创建的图像注释的画布。

this.download = function () {
 var canvas = paper.project.activeLayer.view.element;
 var img = $(canvas).parent().find('img')[0];
 var mergeCanvas = $('<canvas>')
. attr({
 width: $(img).width(),
 height: $(img).height()
 });
 var mergedContext = mergeCanvas[0].getContext('2d');
 mergedContext.clearRect(0, 0, $(img).width(), $(img).height());
 mergedContext.drawImage(img, 0, 0);
 mergedContext.drawImage(canvas, 0, 0);
 self.downloadCanvas(mergeCanvas[0], "image-markup.png");
}

actual Fyrstenberg提供的ingenious解决方案implemented实际下载代码,模拟( <> ) 元素的click事件:

this.downloadCanvas = function (canvas, filename) {
 ///create an"off-screen" anchor tagvar lnk = document.createElement('a'),
 e;
 ///the key here is to set the download attribute of the a tag lnk.download = filename;
 ///convert canvas content to data-uri for link. When download///attribute is set the content pointed to by link will be///pushed as"download" in HTML5 capable browsers lnk.href = canvas.toDataURL();
 ///create a"fake" click-event to trigger the downloadif (document.createEvent) {
 e = document.createEvent("MouseEvents");
 e.initMouseEvent("click", true, true, window,
 0, 0, 0, 0, 0, false, false, false,
 false, 0, null);
 lnk.dispatchEvent(e);
 } elseif (lnk.fireEvent) {
 lnk.fireEvent("onclick");
 }
}

插件的下一个版本将允许下载注释图像( 带有图形的. png 图像和透明 background 上的文本),使程序员可以将它的放置到原始图像中。

结束语

可以看到,在应用程序中有很多空间可以增强。 Paper.js 框架证明了处理复杂图形脚本和事件句柄的任务,同时提供了一套简洁。简单的类和事件集合。

这里外,这一点上应用程序也许太多了,但我认为最大的潜力是如何适应项目的特定需求。 例如它可以是一个严格的教育网站,教师在那里评估学生在线完成的工作。 也可以是一个娱乐游戏,儿童会发现图像对之间的差异。 甚至是在线会议中远程用户的协作应用程序( 像谷歌环聊之类的东西)。

如果你有任何评论,投诉或者建议,请留言 below。 我想从你那里听到,我愿意在新的想法到达时改进应用程序。

历史记录

2014-07-26: 第一个版本。


图像  Html5  MARK  标记  
相关文章