Fabric.js ImageMap &动画编辑器&虚拟设计器

分享于 

39分钟阅读

Web开发

  繁體

imagemapeditor

摘要

本文包括我创建的HTML5 ImageMap编辑器的完整源代码,它允许你从现有图像创建一个图像映射,可以很容易地使用JQuery插件 v6.xml。 另外,还可以创建一个 fabric 画布,它与图像映射完全相同,但比任何图像映射要多。 我会使用新的网站工具和功能更新源代码,并且你可以从我的个人网站下载最新的源代码( 免费),这些源代码在以下位置填充: Software-rus.com

介绍

I,你有一张图片,上面有一些图片,用户可以看到你的图片,你可以在上面画一张图片,你可以在上面画一些图片。 但是在这种情况下,如屋顶或者stonefront绘制的部分,你还需要填充一个概括的区域,ecah Pattern。 最明显的选择是使用房屋的图像地图,用户可以选择不同的颜色和图案,如屋顶,山墙,siding,等等,换句话说,,等。 请参见 https://github.com/jamietre/imagemapster

但是我仍然需要一种方法来创建 <映射> 坐标,这些坐标可以用于ImageMapster插件的语法。 我不喜欢my图形绘图工具或者它的他任何编辑器的图像地图编辑器,因为它们不符合需要。 所以我决定编写我自己的图像映射编辑器,这是本文所包含的编辑器。

我决定使用 Fabric.js, Zaytsev强大的开源JavaScript库来创建我的图像地图编辑器,这是多年来其他许多贡献者都拥有的。 它是在MIT许可下许可的。 示例项目中的" all.js"文件是实际的" fabric.js"库。 fabric 似乎是构建映像编辑器的一种逻辑选择,因为可以轻松地创建和填充画布。 然后使用鼠标缩放。移动和旋转这些对象;修改属性- 颜色。透明。z 索引,等等 还包括SVG-to-canvas解析器。

背景

除了 fabric 我想为控件提供一个简单的工具栏,所以我包含了工具栏。按钮和按钮的Bootstrap 库。 我使用的一些库包括:

  • Fabric.js 库这里库在这里项目的" all.js"文件中找到。 请参见 Fabric.js
  • 下划线带有 80-odd 实用程序功能的库。 请参见下划线
  • Bootstrap。用于创建漂亮的工具栏。 请参见 Bootstrap
  • MiniColors。查找颜色选择器比bootsrap颜色选择器要酷。 请参见 MiniColors
  • 我将很多 Pattern 图像放入下拉列表中,写一个插件,使你可以上下滚动一个下拉列表。
  • 这里编辑器生成 html <映射> 坐标,但不在这里项目中使用这里编辑器。

图像映射,区域分组和元数据选项

对于一个特定图像,它是一个与特定图像相关的坐标列表,它是为了将图像的超链接区域与不同的目标区域进行。 例如,世界上的地图可以能有超链接,以便进一步了解该国家的信息。 图像映射的目的是提供一种简单的方法来连接图像的各个部分,而不用将图像分割成单独的图像文件。

对于使用ImageMapster插件,我们有以下属性:

mapKey: 标识每个imagemap区域的属性。 这是指区域标签上的一个属性,用于在逻辑上对它们进行分组。 任何包含相同mapKey的区域都将被视为组的一部分,当这些区域被激活时,它们将。 可以在mapKey属性中指定多个值,用逗号分隔。 这将导致一个区域成为多个组的成员。 区域在每个组的上下文中可能有不同的选项。 当该区域在物理上悬停时,列出的第一个键将标识对该操作有效的组。 ImageMapster将使用你识别为密钥的任何属性。 如果你要为映射> 坐标生成 HTML,我将"数据-"添加到mapKey的前面,i.e. 是,这将使名称成为HTML文档类型。 例如你可以设置 mapValue: "for'向美国的图像映射,并向提供每个状态的完整 NAME的区域添加属性,比如 data-statename="阿拉斯塔"。或者,如果你可以能拥有的家庭图像映射,比如 data-home1=mapValue,mapValue可以能等于:"屋顶",或者"壁板"。用于住宅或者住宅的等等"状态"一个地图。

mapValue: 一个区域 NAME 或者 id,用于引用地图的给定区域。 例如以下代码定义了一个矩形区域( 9,372,66,397 ),它是房子"屋顶"的一部分:


//mapKey ="home1", mapValue ="roof" and <span class="style2">data-home1="roof"</span>


<img src="someimage.png" alt="image alternative text" usemap="#mapname" />


<map name="mapname">


 <area shape="rect" <span class="style2">data-home1="roof"</span> coords="9,372,66,397" href="#" alt="" title="hover text"/>


</map>



创建 HTML <映射> </map> 代码

编辑editor创建图像映射的html html用户选择"显示图像映射 html。"do我决定使用下划线库 easily html <映射> </Map>的语法。 请记住,我们这里的目标是创建可以复制和粘贴到网站中的html代码,以便在ImageMapster插件中工作。 首先,我使用下划线为 <映射> </map> html创建了一个模板 换句话说,"map_template",如下所示:


<script type="text/underscoreTemplate" id="map_template";>


<map name="mapid" id="mapid">


<% for(var i=0; i<areas.length; i++) { var a=areas[i]; %>&lt;area shape="<%= a.shape %>" 


<%= "data-"+mapKey %>="<%= a.mapValue %;>" coords="<%= a.coords %>" href="<%= a.link %>" alt="<%= a.alt %>"/&gt;


<% } %></map>


</script>



序列化 fabric 画布

我向这里编辑器添加了以下方法,但你需要创建映像映射的唯一方法是"显示图像映射 html":

  • 显示图像映射 Html (。使用下划线模板"map_template"
  • 显示对象的自定义数据(。使用下划线模板"map_data"
  • 显示对象JSON数据( 使用 JSON.stringify(canvas)。 无背景保存
  • 保存JSON本地存储( 使用 JSON.stringify(canvas)。 保存了没有背景的本地存储
  • 加载JSON本地存储( 使用 loadCanvasFromJSONString。 从本地存储加载)

让我们看两种序列化 fabric 画布的方法。 第一个是使用下划线,并使用画布元素的属性来编写自定义数据模板。 第二种方法是使用 JSON.stringify(camvas)。 让我们先看看如何使用下划线。 below 是一个模板示例,用于使用下划线存储属性。


<script type="text/underscoreTemplate" id="map_data">


[<% for(var i=0; i<areas.length; i++) { var a=areas[i]; %>


{


mapKey:"<%= mapKey %>",


mapValue:"<%= a.mapValue %>",


type:"<%= a.shape %>",


link:"<%= a.link %>",


alt:"<%= a.alt %>",


perPixelTargetFind: <%= a.perPixelTargetFind %>,


selectable: <%= a.selectable %>,


hasControls: <%= a.hasControls %>,


lockMovementX: <%= a.lockMovementX %>,


lockMovementY: <%= a.lockMovementY %>,


lockScaling: <%= a.lockScaling %>,


lockRotation: <%= a.lockRotation %>,


hasRotatingPoint: <%= a.hasRotatingPoint %>,


hasBorders: <%= a.hasBorders %>,


overlayFill: null,


stroke:"<#000000>",


strokeWidth: 1,


transparentCorners: true,


borderColor:"<black>",


cornerColor:"<black>",


cornerSize: 12,


transparentCorners: true,


pattern:"<%= a.pattern %>",


<% if ( (a.pattern)!= "" ) { %>fill:"#00ff00",<% } else { 


%>fill:"<%= a.fill %>",<% } %> opacity: <%= a.opacity %>,


top: <%= a.top %>, left: <%= a.left %>, scaleX: <%= a.scaleX %>,


scaleY: <%= a.scaleY %>,


<% if ( (a.shape) == "circle" ) { %>radius: <%= a.radius %>,<% } 


%><% if ( (a.shape) == "ellipse" ) { %>width: <%= a.width %>,


height: <%= a.height %>,<% } 


%><% if ( (a.shape) == "rect" ) { %>width: <%= a.width %>,,


height: <%= a.height %>,<% } 


%><% if ( (a.shape) == "polygon" ) { %>points: [<% for(var j=0; j<a.coords.length-1; j = j+2) { 


var checker = j % 6; %> <% if ( (checker) == 0 ) { 


%>{x: <%= (a.coords[j] - a.left)/a.scaleX %>, y: <%= (a.coords[j+1] - a.top)/a.scaleY %>}, <% } 


else { %>{x: <%= (a.coords[j] - a.left)/a.scaleX %>, y: <%= (a.coords[j+1] - a.top)/a.scaleY %>}, <% }


 } %>]<% } %>},<% } %>


]


</script>



为了加载下划线模板 上面,我们使用 fabric 元素中的相应值来创建一个 array,显示为 below。 请记住我编写了一些属性,以适合我所需要的网站,你可以修改这个需求。


function createObjectsArray(t) {


 fabric.Object.NUM_FRACTION_DIGITS = 10;


 mapKey = $('#txtMapKey').val();


 if ($.isEmptyObject(mapKey)) {


 mapKey = "home1";


 $('#txtMapKey').val(mapKey);


 }



 // loop through all objects & assign ONE value to mapKey


 var objects = canvas.getObjects();


 canvas.forEachObject(function(object){


 object.mapKey = mapKey;


 });


 canvas.renderAll();


 canvas.calcOffset()


 clearNodes();



 var areas = []; //note the"s" on areas!


 _.each(objects, function (a) {


 var area = {}; //note that there is NO"s" on"area"!


 area.mapKey = a.mapKey;


 area.link = a.link;


 area.alt = a.alt;


 area.perPixelTargetFind = a.perPixelTargetFind;


 area.selectable = a.selectable;


 area.hasControls = a.hasControls;


 area.lockMovementX = a.lockMovementX;


 area.lockMovementY = a.lockMovementY;


 area.lockScaling = a.lockScaling;


 area.lockRotation = a.lockRotation;


 area.hasRotatingPoint = a.hasRotatingPoint;


 area.hasBorders = a.hasBorders;


 area.overlayFill = null;


 area.stroke = '#000000';


 area.strokeWidth = 1;


 area.transparentCorners = true;


 area.borderColor = "black";


 area.cornerColor = "black";


 area.cornerSize = 12;


 area.transparentCorners = true;


 area.mapValue = a.mapValue;


 area.pattern = a.pattern;


 area.opacity = a.opacity;


 area.fill = a.fill;


 area.left = a.left;


 area.top = a.top;


 area.scaleX = a.scaleX;


 area.scaleY = a.scaleY;


 area.radius = a.radius;


 area.width = a.width;


 area.height = a.height;


 area.rx = a.rx;


 area.ry = a.ry;


 switch (a.type) {


 case "circle":


 area.shape = a.type;


 area.coords = [a.left, a.top, a.radius * a.scaleX];


 break;


 case "ellipse":


 area.shape = a.type;


 var thisWidth = a.width * a.scaleX;


 var thisHeight = a.height * a.scaleY;


 area.coords = [a.left - (thisWidth/2), a.top - (thisHeight/2), a.left + (thisWidth/2), a.top + (thisHeight/2)];


 break;


 case "rect":


 area.shape = a.type;


 var thisWidth = a.width * a.scaleX;


 var thisHeight = a.height * a.scaleY;


 area.coords = [a.left - (thisWidth/2), a.top - (thisHeight/2), a.left + (thisWidth/2), a.top + (thisHeight/2)];


 break;


 case "polygon":


 area.shape = a.type;


 var coords = [];


 _.each(a.points, function (p) {


 newX = (p.x * a.scaleX) + a.left;


 newY = (p.y * a.scaleY) + a.top;


 coords.push(newX);


 coords.push(newY);


 });


 area.coords = coords;


 break;


 }


 areas.push(area);


 });



 if(t == "map_template") {


 $('#myModalLabel').html('Image Map HTML');


 $('#textareaID').html(_.template($('#map_template').html(), { areas: areas }));


 $('#myModal').on('shown', function () {


 $('#textareaID').focus();


 });


 $("#myModal").modal({


 show: true,


 backdrop: true,


 keyboard: true


 }).css({


 "width": function () {


 return ($(document).width() *. 6) + "px";


 },


 "margin-left": function () {


 return -($(this).width()/2);


 }


 });


 }


 if(t == "map_data") {


 $('#myModalLabel').html('Custom JSON Objects Data');


 $('#textareaID').html(_.template($('#map_data').html(), { areas: areas }));


 $('#myModal').on('shown', function () {


 $('#textareaID').focus();


 });


 $("#myModal").modal({


 show: true,


 backdrop: true,


 keyboard: true


 }).css({


 "width": function () {


 return ($(document).width() *. 6) + "px";


 },


 "margin-left": function () {


 return -($(this).width()/2);


 }


 });


 }


 return false;


};




使用自定义属性序列化 fabric

如果你想使用 JSON.stringify(canvas),那么你需要做一些额外的工作。 关于构建图像地图最重要的一点是,你需要精度最高 10小时或者图像地图不正确。 当你使用下划线时,这个问题并不是因为你读取了位置和点属性,而是使用。 但是 JSON.stringify(canvas)的数据到 2小时的位置,这会导致图像地图中出现剧剧的错误。 我早期意识到这个问题,这就是为什么我最初使用模板方法来提高精确度。 Stefan的一个例子是,Stefan Kienzle kind指出 fabric 有一个解决这个问题的解决方案,你可以如下设置一个 fabric 画布的小数位数:


fabric.Object.NUM_FRACTION_DIGITS = 10;




这解决了使用 JSON.stringify(canvas)的一个问题。 另外一个问题是需要为映像映射和其他属性添加一些自定义属性,通常"stringfy。"通常会在映像映射中添加一些属性,这些属性将包含这些自定义属性的序列化,并添加这些自定义属性。 在 fabric 中添加属性可以是一个现有的元素类型,也可以扩展一般的fabric"元素的toObject"方法。 因为我们的自定义属性需要应用于任何类型的元素,所以子类只是一种类型的元素。 相反,我们可以为其他属性扩展一个通用 fabric"元素的toObject"方法,例如: 为图像映射和 fabric 属性 lockMovementX,lockMovementY,lockScaling和 lockRotation,如下所示,mapKey。链接。alt。mapValue和 Pattern。


canvas.forEachObject(function(object){


 // Bill SerGio - We add custom properties we need for image maps here to fabric


 // Below we extend a fabric element's toObject method with additional properties


 // In addition, JSON doesn't store several of the Fabric properties!!!


 object.toObject = (function(toObject) {


 return function() {


 return fabric.util.object.extend(toObject.call(this), {


 mapKey: this.mapKey,


 link: this.link,


 alt: this.alt,


 mapValue: this.mapValue,


 pattern: this.pattern,


 lockMovementX: this.lockMovementX,


 lockMovementY: this.lockMovementY,


 lockScaling: this.lockScaling,


 lockRotation: this.lockRotation


 });


 };


 })(object.toObject);


. . .


});


canvas.renderAll();


canvas.calcOffset();




fabric background 图像

通过在现有图像上绘制我们的部分,我们将创建图像映射,这是我们创建画布背景的"背景"图像。 我在编辑器中自己的目的是不序列化这里 background 映像。 实际上,为了自己的目的,在序列化之前删除 background 映像并在序列化之后将它的添加回去,以便它不是serialed数据的一部分。 你可以改变它以适合你自己的偏好。 我做这个的原因之一是 background 图像的truetype序列化,除非你还原相同的路径。 我需要一个 relative 路径来实现我自己的目的。 "背景"图像按如下方式添加。


canvas.setBackgroundImage(backgroundImage, canvas.renderAll.bind(canvas));




Bootstrap的导航栏

我想将所有控件放在一行中,以允许编辑尽可能多的空间。 我决定使用 Bootstrap 库和 bootstratp"导航栏"来获得干净的外观,如下所示。



导航栏的功能从左至右:

  • 保存这里下拉菜单包括几个保存&还原选项。
  • 圆。将 fabric 圆元素添加到画布。
  • Elipsis。将 fabric elipse元素添加到画布。
  • rectangle。将一个 fabric rectangle 元素添加到画布中,边的( 例如,一个正方形) 相等。
  • 当单击 Polygon"多边形"icon adds向画布添加一个 fabric"打开"ploygon元素。
    每次单击画布,都会在打开的多边形中添加一个新节点。 要关闭多边形,只需单击
    仅在多边形打开时出现的"关闭多边形"符号( 这里未显示)。
  • 当单击"字母"icon 时,将向画布添加一个 fabric 文本元素。
  • 当单击鼠标按钮,单击鼠标右键,然后单击鼠标右键,然后单击所有对象,锁定所有对象,锁定所有对象。锁定所有对象等。
  • 如果单击"属性"icon,则显示选定的fabric 元素的属性列表。
  • 动画。"目标"icon 演示一些典型的fabric 动画。
  • 不透明度。单击"已经检查"icon 时会更改选定的fabric 元素的不透明度。
  • 颜色选择器。允许你更改选定 fabric 元素的颜色。 我没有使用 Bootstrap 选择器的颜色 !
  • 缩放。单击"放大"icon 时显示缩放和输出的控件。
  • 区域。显示地图区域。换句话说,。fabric 元素mapValues的列表,这些区域是ImageMapster的区域。
  • mapValues下拉列表这里下拉列表显示了画布中所有元素的当前mapValues列表。
    记住ImageMapster中的DataKey是带有"数据-"的mapValue,它可以满足HTML5的兼容性。
  • 刷新。单击 icon 时,通过读取画布中元素的mapValue属性的值来构建MapValues下拉列表。
    这是imagemapster的插件中用于图像映射的MapKey Id的值。
  • 模式"图案"下拉框显示图像模式的滚动列表。 我编写了插件 换句话说,。scrollMenu。js,使它的易于显示
    通过滚动菜单在菜单中列出的项目列表。 模式被应用到与MapValues下拉列表中选定的mapValue匹配的所有元素 MapValues。

后来添加 zoom zoom我还修改了导航栏控件,以便在添加多边形的节点时仍然可以单击导航栏,而在页面中添加了 scrooled。 要完成我使用的Bootstrap'navbar-fixed-top'类,请执行以下操作:


<nav class="navbar navbar-fixed-top">


 <div class="navbar-inner">


. . . etc.



操作图像映射的fabric 画布元素

请记住,这个编辑器不是一般的编辑器或者绘图程序。 创建它是为了做一件事,为图像映射创建 html。 工具栏包括标准图像映射中的所有基本几何图形,包括圆形。椭圆。rectangle 和多边形。 我添加了文本只是演示,但文本不是标准图像映射的一部分。 读者可以自由地添加其他 fabric 形状和选项。

我们在 fabric 画布上监听mousedown事件,如下所示:


 var activeFigure;


var activeNodes;


canvas.observe('mouse:down', function (e) {


 if (!e.target) {


 add(e.e.layerX, e.e.layerY);


 } else {


 if (_.detect(shapes, function (a) { return _.isEqual(a, e.target) })) {


 if (!_.isEqual(activeFigure, e.target)) {


 clearNodes();


 }


 activeFigure = e.target;


 if (activeFigure.type == "polygon") {


 addNodes();


 }


 $('#hrefBox').val(activeFigure.link);


 $('#titleBox').val(activeFigure.title);


 $('#groupsBox').val(activeFigure.groups);


 }


 }


});




当用户单击工具栏上的圆圈时,将设置为equal对象的图形类型,我们将在画布上开始添加这些对象,因为我们将移动这些对象并使它的精确地指向图像地图的区域。 然后当用户单击画布时,选中的图形类型将使用以下方法添加到画布。 请记住,我创建这个编辑器是为了满足我在创建图像映射方面的即时需要。 你可以轻松定制这里编辑器的特性,以满足你自己的需要或者偏好。


function add(left, top) {


 if (currentColor.length <2)


 {


 currentColor = '#fff';


 }



 if ((window.figureType === undefined) || (window.figureType == "text"))


 return false;



 var x = (window.pageXOffset!== undefined)? window.pageXOffset : (document.documentElement || document.body.parentNode || document.body).scrollLeft;


 var y = (window.pageYOffset!== undefined)? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;



 //stroke: String, when 'true', an object is rendered via stroke and this property specifies its color


 //strokeWidth: Number, width of a stroke used to render this object



 if (figureType.length> 0) {


 var obj = {


 left: left,


 top: top,


 fill: ' ' + currentColor,


 opacity: 1.0,


 fontFamily: 'Impact',


 stroke: '#000000',


 strokeWidth: 1,


 textAlign: 'right'


 };



 var objText = {


 left: left,


 top: top,


 fontFamily: 'Impact',


 strokeStyle: '#c3bfbf',


 strokeWidth: 3,


 textAlign: 'right'


 };



 var shape;


 switch (figureType) {


 case "text":


 //var text = document.getElementById("txtAddText").value;


 var text = gText;


 shape = new fabric.Text ( text, obj);


 shape.scaleX = shape.scaleY = canvasScale;


 shape.lockUniScaling = true;


 shape.hasRotatingPoint = true;


 break;


 case "square":


 obj.width = 50;


 obj.height = 50;


 shape = new fabric.Rect(obj);


 shape.scaleX = shape.scaleY = canvasScale;


 shape.lockUniScaling = false;


 break;


 case "circle":


 obj.radius = 50;


 shape = new fabric.Circle(obj);


 shape.scaleX = shape.scaleY = canvasScale;


 shape.lockUniScaling = true;


 break;


 case "ellipse":


 obj.width = 100;


 obj.height = 50;


 obj.rx = 100;


 obj.ry = 50;


 shape = new fabric.Ellipse(obj);


 shape.scaleX = shape.scaleY = canvasScale;


 shape.lockUniScaling = false;


 break;


 case "polygon":


 //$('#btnPolygonClose').show();


 $('#closepolygon').show();



 obj.selectable = false;


 if (!currentPoly) {


 shape = new fabric.Polygon([{ x: 0, y: 0}], obj);


 shape.scaleX = shape.scaleY = canvasScale;


 lastPoints = [{ x: 0, y: 0}];


 lastPos = { left: left, top: top };


 } else {


 obj.left = lastPos.left;


 obj.top = lastPos.top;


 obj.fill = currentPoly.fill;


 // while we are still adding nodes let's make the element


 // semi-transparent so we can see the canvas background


 // we will reset opacity when we close the nodes


 obj.opacity =. 4;


 currentPoly.points.push({x: left-lastPos.left, y: top-lastPos.top });


 shapes = _.without(shapes, currentPoly);


 lastPoints.push({ x: left - lastPos.left, y: top-lastPos.top })


 shape = repositionPointsPolygon(lastPoints, obj);


 canvas.remove(currentPoly);


 }


 currentPoly = shape;


 break;


 }



 shape.link = $('#hrefBox').val();


 shape.alt = $('#txtAltValue').val();


 mapKey = $('#txtMapKey').val();


 shape.mapValue = $('#txtMapValue').val();



 // Bill SerGio - We add custom properties we need for image maps here to fabric


 // Below we extend a fabric element's toObject method with additional properties


 // In addition, JSON doesn't store several of the Fabric properties!!!


 shape.toObject = (function(toObject) {


 return function() {


 return fabric.util.object.extend(toObject.call(this), {


 mapKey: this.mapKey,


 link: this.link,


 alt: this.alt,


 mapValue: this.mapValue,


 pattern: this.pattern,


 lockMovementX: this.lockMovementX,


 lockMovementY: this.lockMovementY,


 lockScaling: this.lockScaling,


 lockRotation: this.lockRotation


 });


 };


 })(shape.toObject);


 shape.mapKey = mapKey;


 shape.link = '#';


 shape.alt = '';


 shape.mapValue = '';


 shape.pattern = '';


 lockMovementX = false;


 lockMovementY = false;


 lockScaling = false;


 lockRotation = false;


 canvas.add(shape);


 shapes.push(shape);


 if (figureType!= "polygon") {


 figureType = "";


 }


 } else {


 deselect();


 }


}




将模式应用于画布元素

许多虚拟设计网站不仅需要应用颜色到地图区域,还需要应用 Pattern 和颜色。 below 是我创建的两种方法,用于将模式应用到 fabric 画布中的fabric 元素。


 //"title" is the mapValue &"img" is the short path for the pattern image


function SetMapSectionPattern(title, img) {


 canvas.forEachObject(function(object){


 if(object.mapValue == title){


 loadPattern(object, img);


 }


 });


 canvas.renderAll();


 canvas.calcOffset()


 clearNodes();


}



function loadPattern(obj, url) {


 obj.pattern = url;


 var tempX = obj.scaleX;


 var tempY = obj.scaleY;


 var zfactor = (100/obj.scaleX) * canvasScale;



 fabric.Image.fromURL(url, function(img) {


 img.scaleToWidth(zfactor).set({


 originX: 'left',


 originY: 'top'


 });



 // You can apply regualr or custom image filters at this point


 //img.filters.push(new fabric.Image.filters.Sepia(),


 //new fabric.Image.filters.Brightness({ brightness: 100 }));


 //img.applyFilters(canvas.renderAll.bind(canvas));


 //img.filters.push(new fabric.Image.filters.Redify(),


 //new fabric.Image.filters.Brightness({ brightness: 100 }));


 //img.applyFilters(canvas.renderAll.bind(canvas));



 var patternSourceCanvas = new fabric.StaticCanvas();


 patternSourceCanvas.add(img);



 var pattern = new fabric.Pattern({


 source: function() {


 patternSourceCanvas.setDimensions({


 width: img.getWidth(),


 height: img.getHeight()


 });


 return patternSourceCanvas.getElement();


 },


 repeat: 'repeat'


 });


 fabric.util.loadImage(url, function(img) {


 // you can customize what properties get applied at this point


 obj.fill = pattern;


 canvas.renderAll();


 });


 });


}




滑动图案下降

由于在任何虚拟设计器中有许多可以能的Pattern 图像,我向编辑器中的模式添加了 slider。 在你首先需要单击工具栏中的刷新符号时,你首先需要单击工具栏上的刷新符号,然后将 Pattern 应用到屏幕上的下拉列表中,以应用。"mapValue"。 然后从mapValues下拉列表中选择一个 mapVlue。 接下来你可以从模式下拉列表中选择一个 Pattern,它将应用到你选择的mapValue中的所有对象。 我创建了一个简短的视频,以说明这个在的YouTube。

添加缩放是必须的,但它创建了一些新问题 !

一旦我开始使用图像地图编辑器,我就快速意识到我必须添加缩放。 我的图像地图有一些很小的区域,我需要创建多边形,所以我添加了放大地图的能力,如下图所示。


 // Zoom In


function zoomIn() {


 // limiting the canvas zoom scale


 if (canvasScale <4.9) {


 canvasScale = canvasScale * SCALE_FACTOR;



 canvas.setHeight(canvas.getHeight() * SCALE_FACTOR);


 canvas.setWidth(canvas.getWidth() * SCALE_FACTOR);



 var objects = canvas.getObjects();


 for (var i in objects) {


 var scaleX = objects[i].scaleX;


 var scaleY = objects[i].scaleY;


 var left = objects[i].left;


 var top = objects[i].top;



 var tempScaleX = scaleX * SCALE_FACTOR;


 var tempScaleY = scaleY * SCALE_FACTOR;


 var tempLeft = left * SCALE_FACTOR;


 var tempTop = top * SCALE_FACTOR;



 objects[i].scaleX = tempScaleX;


 objects[i].scaleY = tempScaleY;


 objects[i].left = tempLeft;


 objects[i].top = tempTop;



 objects[i].setCoords();


 }


 canvas.renderAll();


 canvas.calcOffset();


 }


}




我快速注意到,当我放大画布并且页面滚动到一个导航按钮,窗口将滚动到顶部。 有几种方法可以修复这个问题,但我决定使用工具栏上的按钮链接,以防止单击浏览器窗口。





href="javascript:void(0)" 



我遇到的下一个问题是 fabric 对象的缩放因子或者scaleX和 scaleY。 如果添加到画布的所有 fabric 对象都有 scaleX = 1.0和 scaleY = 1.0,那么就可以正常工作。 但是如果缩放并添加对象,那么这些比例值不是 1,而且在保存和恢复映射时得到了一点 trixky。 i 指出最好的事情是确保整个画布的缩放速度为:1的正常设置。 因为我们恢复svaed映射时,我们总是将保存的对象恢复到 1: 1的画布上。

我有一个顿悟- fabric 比图像映射更好 !

开始写这个图像地图编辑器时我只使用 fabric 创建编辑器,所以我可以创建ImageMapster插件的图像映射。 然后,在编写这个编辑器的过程中,我出现了一个顿悟 ! 我发现使用 fabric 画布作为"映像映射"远比使用标准图像映射要好得多 ! 换句话说,我可以把图像分成几部分。换句话说,。"mapvalues"和颜色,将模式添加到这些部分。 所以你可以自由使用并定制这个编辑器来创建标准图像映射或者创建比标准图像映射更具有更多功能的fabric"映像映射"。

使用代码

使用这个编辑器有两种方法,即创建 <地图> html用于 ImageMapster,或者创建与图像映射完全一样的fabric 画布,但是有许多功能。 使用ImageMapster需要注意一点,即 ImageMapster's"p 函数(。上下文,图像,mapArea,选项) {" 函数not使用"Pattern"向图像映射区域中的区域应用来实现对大区域的function。 因这里,我想指出,你需要修改"imagemapster的。addaltimage"或者向imagemapster的插件添加新函数,以完成如下操作:


// Add a function like this to the ImageMapster plugin to apply"patterns" to map sections


p.addPatternImage = function (context, image, mapArea, options) {


 context.beginPath();


 this.renderShape(context, mapArea);


 context.closePath();


 context.save();


 context.clip();


 context.globalAlpha = options.altImageOpacity || options.fillOpacity;


 //you can replace the line below with one that positions a smaller pattern reactangular exactly over map area to save memory


 context.clearRect(0, 0, mapArea.owner.scaleInfo.width, mapArea.owner.scaleInfo.height); // Clear the last image if it exists.


 var pattern = context.createPattern(image, 'repeat'); // Get the direction from the button.


 context.fillStyle = pattern; // Assign pattern as a fill style.


 context.fillRect(0, 0, mapArea.owner.scaleInfo.width, mapArea.owner.scaleInfo.height); // Fill the canvas.


};




Map2JSON

我还添加了一个文件,换句话说,map2json.htm,,用示例映像映射和代码将现有图像映射转换为 fabric 画布。 你必须将代码修改为更改变量名,尽管你自己的变量名为。

Points of Interest

如前所述,当我意识到可以使用 fabric 画布替换旧图像映射,但这个编辑器会做任务。 另外,如 上面 所提到的,使用"javascript: void ( 0 ) 而不是"#"阻止滚动在导航栏上是一个非常有用的提示。
虽然我使用 Visual Studio作为web编辑器,但是编辑器本身只是普通的"html"文件 i.e.,可以双击并在任何浏览器中运行,以使用它。

Chrome 框架插件我建议安装 Chrome 框架插件插件: 使用 Chrome 框架插件的优点是,一旦安装了 IE,就支持旧版本 IE的最新的HTML。 这个插件对网络开发者有好处,它允许他们使用现代的网络功能来编写应用程序,而不必让 IE 用户在后台使用web功能。 只要考虑一下web开发人员保存的时间,而不需要编码 IE 和解决方案。

结束语

你可以自己决定哪个更好,Imagemapster和一个标准图像映射,或者使用带有 fabric 对象的fabric 画布,添加更多的酷特性。 当然,这取决于你的需求和客户想要什么 ! 至少这个编辑器将允许你创建这两者并对它们进行测试。 enjoy!


图像  DES  EDI  设计  动画  Virtual  
相关文章