热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

基于SVG的票面设计器开发总结

今天周六,原则上要休息,但想到下周还有一堆任务,还是先做一部分工作吧,就把之前做的票面设计器改了改,增加了上传图片和更换背景底图的功能。现在打算整理下这个设计器,也算对齐一个总结。不过这属于我们部门的

今天周六,原则上要休息,但想到下周还有一堆任务,还是先做一部分工作吧,就把之前做的票面设计器改了改,增加了上传图片和更换背景底图的功能。现在打算整理下这个设计器,也算对齐一个总结。不过这属于我们部门的产品,代码我使用截图的方式多一些。首先来看一下我做的这个票面设计器的最终效果图:


从截图中可以看到在页面里,专业点叫我在画布里增加了很多的元素,这些元素都与剧院票务相关包括项目、场次、地点,二维码等信息。上面的word截图是我今天做的功能,背景图我也换成了自定义的图片。光看这个页面其实不难做,因为页面只是票面设计的一部分,还有后台的支持,票面保存与获取,打印等一系列的流程,本篇我侧重于前端的开发,也就是基于SVG如何完成一个简单票面设计器。


关于SVG的基础,这里我就不再讲了,因为之前我也写过关于SVG入门的文章,传送门:http://blog.csdn.net/qq522935502/article/details/45073897  

首先我们来分析下这个票面设计器有哪些功能:

(1)工具栏点击后,需要在票面增加对应的元素,放置在初始的坐标。

(2)点击元素本身,可以对其进行拖拽移动,字体调整,自动对齐等功能。

(3)每个元素都应该有自己的属性包括:类型,ID,名称,标识等。


讲解之前,大家还是要对SVG的基础知识有一些了解,例如:

(1)组的概念。在本篇中,我们会使用很多组,因为组可以很简单的将一组元素合为一个组,进行同一个样式修改或者旋转移动比较方便。

(2)创建元素的方法,例如创建一个组:var group = document.createElementNS(globalVb.svgNS,"g");(svgNS = svgNS: "http://www.w3.org/2000/svg")

创建一个文本元素:document.createElementNS(globalVb.svgNS, "text");等。

(3)坐标系:关于SVG的坐标系统其实是比较复杂的,默认情况下SVG坐标系的原点(0,0)与左上角,与屏幕的左上角是完全重合的,此时SVG坐标系同用户坐标系保持一致没有发生变形移位的现象。SVG的坐标系统也是由横X,纵Y和原点组成,SVG坐标系统称作为工作区坐标系统或者矩阵坐标系统。在实际开发中由于我们会将我们的画布放置在屏幕中心,坐标原点(0,0)也不就在屏幕左上角了,这个大家要明白。

(4)关于元素移动,有两种方式,一个是用绝对坐标,一个是用相对坐标。从实现角度来讲后者更简单,但从扩展角度或者说后期想做一些更高级的元素的话绝对坐标就比较有优势了。但目前咱们没有特别高级的功能就是移动所以使用后者也就是用相对坐标,那相对于谁的坐标呢,就是元素原始的绝对坐标。另外我们还需要明确的一个问题就是如果一个元素的初始坐标x是100 ,我通过方法对元素进行移动,使其像右边移动了20像素。到最后我们计算这个元素的坐标的时候要记得要将原始坐标与移动坐标相加。也就是100+20是120,如果我们使其向左移动的20,也就是相对于100,我们减少了20,它的最终绝对坐标是80.


好,说明白了这些问题以后,我们开始看看,怎么完成这样一个设计器。


1、第一步,我们要定义一个常量,这些常量的类型主要包括以下:

(1)系统支持哪些业务变量,这些业务变量是否可以分类,是的,我们目前支持关于项目的、关于订单的、关于场馆的元素:generalKeyId:"projectKey,orderKey,siteKey",具体包括:generalKeys:"projectName,showTime,priceMoney,priceName,ticketCodeId,orderId,placeName,siteName,floorName,boundName,seatRow,seatNo",这些元素我不一一解释了就,除了这些变量还有一些常量例如:imageKey,在image里有3中类型:imageKeys:"qrcode,image,map", 分别是二维码,图片和底图。每个元素的初始位置是:transform:"translate(0,0) rotate(0) scale(1)", 我们将这些信息统一放置在常量里:


(2)我们需要在系统中增加一些鼠标操作,比如鼠标点击,鼠标划过,鼠标放下等操作,此时我们需要根据元素的一些特殊信息来判断元素属于哪种类型,一般我们会根据元素的class样式来判断,所以为每一个元素都定义了对应的class名字,另外,这些元素在初始、选中等状态下都有不同的边框颜色,他们在页面里显示的类型共计5种分别是静态文本,动态文本,二维码,图片以及底图:



2、第二步,进入票面设计器,我们需要对票面设计器进行一些初始化的工作,例如如果当前我们是编辑状态,那需要通过后台加载票面并通过SVG解析到页面来显示,如果是新建状态,则我们需要初始化工具栏的这些下拉框,选择框等UI组件的状态等等。所以在进入票面设计器时,我们设计了这样一个方法:

function initTicketFace(data,info,clear),它接受3个参数<1>data 数据,主要是指票面数据,一遍是用户点击编辑时才有数据 <2> info 主要是一些辅助信息 例如票面的名称等 <3>clear是一个命令 意味是否清除当前的画布信息:

/**
* 初始化方法
*/
function initTicketFace(data,info,clear){
console.log("-----------initTicketFace------------");
try {
$scope.tplName = "["+data.name+"]";
$scope.currentTpl = data;
if(clear){
jquerySvg.empty();
}
if(data == null){
return;
}
baseInfo = info;
initEventListener();//初始化工具栏的鼠标事件等
resolveTicketFace(data);//如果票面数据不为空 则进行解析回显
} catch (e) {
console.log(e);
}
};

3、忘记了截图我们的工具栏是怎么写的,这些工具栏的按钮其实就是一些select 下拉框,在form里放置着例如:



通过为这些select组件增加点击事件,来触发对应的方法,来完成图形的创建以及往画布上添加。这些事件我们是如何进行添加的呢?看下面的代码:

/**项目键发生变化*/
$("select").unbind("change");
$("select").change(keyChangeFun);


截图里我也写的很清除了,点击了元素后,元素会根据各自的类型初始化对应的信息,包括每个元素都有自身的XY坐标位置,class,文本示例内容等信息,接下来我们就来看一下这个commonAdd方法都做了什么,这个方法是一个核心的方法。


4、commonAdd方法,向画布添加元素:

var commOnAdd= function(loadGroup,element,loadRect) 首先 这个方法我们接受3个参数,第一个loadGroup 指所添加元素的组; element 是指元素本身,loadRect是指元素的辅助矩形。如果我们是新增一个元素自然第一个参数和第三个参数传null,如果是回显元素,这3个参数都是有值的。


进入方法:

(1)如果参数类型是“背景底图”。 则我们无须将其添加到画布里,而是直接将图片填充到背景色里即可,这里我们使用的方案是填充图片base64编码,现在HTML都是支持的,例如:


这里我们需要了解的是,base64简单来说,它把一些8-bit数据翻译成了标准的ASCII字符,网上有很多免费的base64编码和解码的工具,目前IE8,Firfox,chirome,苹果浏览器等都支持。而且我们也需要了解这种格式:在上面的Data URI中,data表示取得数据的协定名称,image/jpg是指数据类型名称,base64是数据的编码方法,逗号后面就是这个image文件base64编码后的数据。


(2)如果参数类型是非背景底图,则我们需要为其增加一个组,即每个元素都规定在一个组下,然后给这个组设置它的class,transform,以及组ID等信息。当然这里要区分是新增还是解析:


(3)如果参数类型是静态文本或者动态文本(静态文本是指写死的信息,动态文本是指会根据具体实际的票打印不同的内容)。则我们创建一个TEXT的文本节点,设置它的相关参数:


二维码和图片这里我们也不重点解说。完成上述的节点设置后,我们要对他们的一些共同属性进行设置,包括坐标,文本内容等:


这样我们的元素就构造结束了,下一步,就是将元素放置在组里,再将组添加到画布中。完成上述工作后,我们再需要为这个元素增加一个辅助的矩形便于我们进行拖拽,因为之后我们要进行鼠标的拖拽,矩形的感知力要比文本好很多。


这里注意,我们将矩形的透明度设置为了0.意思就是其实用户看到这个矩形,它只是辅助的作用,不过边框我们可以看到。然后依次的添加到画布中:

group.appendChild(keyElement);
group.appendChild(assistRect);
scriptSvg.appendChild(group);


这样其实就完成了我们的一个核心功能:如果向SVG画布里添加元素信息。


5、元素的鼠标点击事件。


首先我们需要为画布添加一个鼠标按下事件:

jquerySvg.unbind("mousedown");
jquerySvg.bind("mousedown",mousedownFun);

function mousedownFun(evt){
hideDialog();
var target = evt.target;
curLocation.x = evt.clientX;
curLocation.y = evt.clientY;
var className = target.getAttribute("class");

通过上述代码,我们可以获取鼠标目前的坐标位置,以及选中的图形的class属性,这个class属性我们之前设置过,不同的元素有不同的classname,所以只要我们判断不同的classname就可以做对应的操作处理了:



再看一下我们的辅助线是怎么做的:


关于PATH:



6、接下来我们节约时间,再讲一个拖拽功能,刚才我们在选中图形后,为发布增加一个鼠标移动的事件,其实就是为拖动图形做准备。通过监听鼠标的位置,动态的改变图形的x和y坐标来实现图形的平移,不过这里我们需要注意的是,我们最开始说了我们使用的是相对坐标,即是通过修改图形的transform属性中的translate参数来进行平移的。关于transform属性的其他特性,我们可以参考这个网址看一下:http://blog.163.com/asdf_zhy/blog/static/274596872010102224240426/




7、鼠标抬起事件。当鼠标抬起后,我们要做的工作是将图形的相对坐标换算为绝对坐标。这么做的好处是方便下次对图形进行移动时进行计算。如果我们不去计算,则图形再进行集体平移,或者自动对齐功能时,它的计算公式将非常复杂,如果还原了则会简单很多。



8、功能部分其实就差不多这些,相信如果你和我是同事,看了我这些代码是不是理解起来就简单多了。再讲一个对齐吧,对齐其实我采用了一种简单的方法,即直接修改元素的绝对坐标。因为我们在鼠标松开的时候还原了元素的transform属性,这是我们修改绝对坐标不需要考虑transform。比较方便。例如左对齐:






9、保存当前的票面信息。我们写了这么多代码最终的目的就是为了将其保存起来,在需要的时候再读取并打印。如何保存呢,没有用数据库,而是放置在了磁盘里,将这些信息转化为JSON保存在了txt里,需要的时候直接使用jackson的方法,readWithFile就可以了:

/**
* 读取票面方法
*/
function readTicketFace(){
console.log("------------readStart------------");
var groupList = jquerySvg.find("."+classes.groupClass);
var ticketGroupList = new Array(); //返回值
for(var i=0;ivar group = groupList[i];
var groupObj = {
"clazz":group.getAttribute("class"),
"transform":group.getAttribute("transform"),
"groupId":group.getAttribute("groupId")
};
var rect = $(group).children("rect")[0];
groupObj.elementRect = {
"clazz":rect.getAttribute("class"),
"x":rect.getAttribute("x"),
"y":rect.getAttribute("y"),
"width":rect.getAttribute("width"),
"height":rect.getAttribute("height"),
"fillOpacity":rect.getAttribute("fill-opacity"),
"stroke":"none",
"rectId":rect.getAttribute("rectId")
};
var text = $(group).children("text")[0];
var image = $(group).children("image")[0];
if(text){
groupObj.elementKey = {
"width":"",
"height":"",
"x":text.getAttribute("x"),
"y":text.getAttribute("y"),
"fontWeight":text.getAttribute("fontWeight") ? text.getAttribute("fontWeight"):"",
"fontItalic":text.getAttribute("fontItalic") ? text.getAttribute("fontItalic"):"",
"style":text.getAttribute("style")? text.getAttribute("style"):"",
"format":text.getAttribute("format"),
"content":text.getAttribute("content"),
"fontSize":text.getAttribute("font-size"),
"fontFamily":text.getAttribute("font-family"),
"textAnchor":text.getAttribute("text-anchor"),
"clazz":text.getAttribute("class"),
"elementType":text.getAttribute("elementType"),
"isStatic":text.getAttribute("isStatic"),
"keyName":text.getAttribute("keyName"),
"signId":text.getAttribute("signId"),
"keyId":text.getAttribute("keyId")
};
}else if(image){
groupObj.elementKey = {
"x":image.getAttribute("x"),
"y":image.getAttribute("y"),
"height":image.getAttribute("height"),
"width":image.getAttribute("width"),
"content":image.getAttribute("content"),
"clazz":image.getAttribute("class"),
"elementType":image.getAttribute("elementType"),
"isStatic":image.getAttribute("isStatic"),
"keyName":image.getAttribute("keyName"),
"signId":image.getAttribute("signId"),
"keyId":image.getAttribute("keyId")
};
}
ticketGroupList.push(groupObj);
}
console.log("------------readEnd------------");
return ticketGroupList;
};


后台很简单:

/**
* 另存为模版
* @return
*/
public static CommonResult saveAsTpl(SVGTicketFace svgTicketFace){
CommonResult commOnResult= new CommonResult();
try {
/**
* 另存
*/
String time = STARTWITHS + DateUtil.getNowByFormat("ddHHmmss");
svgTicketFace.setId(time);
if(StringUtils.isBlank(svgTicketFace.getName())){
svgTicketFace.setName("模版"+time);
}

commOnResult= isExistByName(svgTicketFace.getName());
if(!commonResult.isOk()){
return commonResult;
}

String fileName = configPath + time +".txt";
File file = new File(fileName);
file.createNewFile();
objectMapper.writeValue(file,svgTicketFace);

/**
* 插入记录
*/
svgTicketFace.setVersion(EDIT_VERSION);
insert(svgTicketFace);

commonResult.setOk(true);
commonResult.setDesc("保存成功,票面名称["+svgTicketFace.getName()+"]");
commonResult.setData(time);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage());
}
return commonResult;
}


10、读取票面并解析回显。这个代码我就不贴了,很简单其实,直接调现成的commonAdd就可以了。


附加-上传图片功能:


将图片的BASE64编码放置在文本框内,点击上传,创建对应的元素信息,直接添加到画布即可,代码如下:

if(type == elementType.image){
keyElement = document.createElementNS(globalVb.svgNS, "image");
keyElement.setAttributeNS(null,"height",element.height);
keyElement.setAttributeNS(null,"width",element.width);
keyElement.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",element.content);
checkFlag = 2;
}





如此,整个基于SVG的票面设计器也就这么讲完了,相对如果你对svg和Javascript比较熟悉一定不会觉得很难,甚至会发现我写的代码其实很矬,请见谅。最近公司来了几个新同事,可以看看我最近整理的这些总结的笔记,会很快上手工作的。看明天是否有时间,如果有就整理关于quartz定时器的代码如果没有时间就放在下周去整理。近期我还会阅读关于多线程方面的数据,有收获后也会写博客的,明天可能会去吃大虾,哈哈。



推荐阅读
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • 如何去除Win7快捷方式的箭头
    本文介绍了如何去除Win7快捷方式的箭头的方法,通过生成一个透明的ico图标并将其命名为Empty.ico,将图标复制到windows目录下,并导入注册表,即可去除箭头。这样做可以改善默认快捷方式的外观,提升桌面整洁度。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • Metasploit攻击渗透实践
    本文介绍了Metasploit攻击渗透实践的内容和要求,包括主动攻击、针对浏览器和客户端的攻击,以及成功应用辅助模块的实践过程。其中涉及使用Hydra在不知道密码的情况下攻击metsploit2靶机获取密码,以及攻击浏览器中的tomcat服务的具体步骤。同时还讲解了爆破密码的方法和设置攻击目标主机的相关参数。 ... [详细]
  • 本文介绍了Perl的测试框架Test::Base,它是一个数据驱动的测试框架,可以自动进行单元测试,省去手工编写测试程序的麻烦。与Test::More完全兼容,使用方法简单。以plural函数为例,展示了Test::Base的使用方法。 ... [详细]
  • C# 7.0 新特性:基于Tuple的“多”返回值方法
    本文介绍了C# 7.0中基于Tuple的“多”返回值方法的使用。通过对C# 6.0及更早版本的做法进行回顾,提出了问题:如何使一个方法可返回多个返回值。然后详细介绍了C# 7.0中使用Tuple的写法,并给出了示例代码。最后,总结了该新特性的优点。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
author-avatar
蟹子的宿命
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有