采用React + Fabric + ImageMagick 实现大图片DIY定制
一,需求背景:
某个印刷公司,有一系列的设计文件模板。接到客户订单时,就在这些设计文件模板上,做一些简单的定制,就能够满足客户的印刷需求。 如在设计文件模板上添加客户的Logo,二维码,联系方式等。
1,面临困境:
a,每天有上千个模板文件需要加Logo,文字。印刷公司不得不请几个设计师来完成这项工作。
b,设计师要不断的与客户沟通,如文字颜色,字体,文字大小,二维码, Logo的位置。
c,设计文件不能统一归档存储,时间久了容易丢失。
d,工作枯燥泛味(在模板文件上更换Logo,添加文字)。
2,解决方案:
a,公司决定开发一个网站,公布设计文件模板,让客户挑选心仪的文件模板。
b,让客户自己上传 Logo,二维码,添加文字等信息。
c,系统自动保存客户的设计文件,并与销售订单自动关联。
二,需求转换为软件原型
1,图片需求:
a,可以在工具栏中,往设计图片上添加Logo,二维码等图片信息
b,对上传的图片要进行移动,放大,缩小,旋转
c,可以预览,删除上传的图片
2,文字需求:
a,可以添加与定义文字信息
b,文字大小,字体,颜色可自定义
c,文件可以移动,旋转等功能
d,可以删除文字。
3,软件原型:

三,技术选型与实现
1,技术选型:
a,印刷行业的图片都非常大,小则几M,大则几十M,上百M。在网页上只能操作缩略图,然后再生成原图。
b,要对图片,文字进行移动,旋转,放大缩小,我选择了Fabric类库。
c,前端框架主要有3种Vue.js ,React ,Angular 。对我个人来说Vue.js是我最熟悉的框架之一,但是 Vue.js + Fabric 的组合没有 React + Fabric 成熟。最终我选择了 React + Fabric的组合。
d,把设计好的图片,文字信息以Json的数据推送到服务器,通过ImageMagick 技术生成印刷原图。
2,代码实现:
前端关键代码:
a,图片的移动,放大缩小,旋转实现:

 
                img.on('selected',(e) => {
                });
                img.on('moved',(e) => {
                    if(e.target) {
                        let selectObj = this.props.templateModel.rows.filter(d => {return d.uid === e.target.uid})[0];
                        if(selectObj) {
                            selectObj.top = e.target.top;
                            selectObj.left = e.target.left;
                            this.UpdateItemByChild(selectObj);
                        }
                    }
                })
                img.on('rotated',(e) => {
                    if(e.target) {
                        let selectObj = this.props.templateModel.rows.filter(d => {return d.uid === e.target.uid})[0];
                        if(selectObj) {
                            selectObj.angle = e.target.angle;
                            var rect = e.target.getBoundingRect();
                            selectObj.top = rect.top;
                            selectObj.left = rect.left;
                            selectObj.width = e.target.width;
                            selectObj.height = e.target.height;
                            this.UpdateItemByChild(selectObj);
                        }
                    }
                })
                img.on('scaled',(e) => {
                    if(e.target) {
                        let selectObj = this.props.templateModel.rows.filter(d => {return d.uid === e.target.uid})[0];
                        if(selectObj) {
                            if(e.target.scaleX) {
                                selectObj.scaleX = e.target.scaleX;
                            }
                            if(e.target.scaleY) {
                                selectObj.scaleY = e.target.scaleY;
                            }
                            selectObj.width = e.target.width;
                            selectObj.height = e.target.height;
                            selectObj.top = e.target.top;
                            selectObj.left = e.target.left;
                            this.UpdateItemByChild(selectObj);
                        }
                    }
                });
b,图片在称动,放大缩小,旋转时的位置与大小约束:

 
                img.on('rotating',(e) => {
                });
                img.on('scaling',(e) => {
                    var maxWidth = fixedInfo.width;
                    var maxHeight = fixedInfo.height;
                    if(e.transform.action === 'scaleX' || e.transform.action === 'scaleY' || e.transform.action === 'scale'){
                        if(img.width * img.scaleX >= maxWidth){
                            img.scaleX = maxWidth / img.width;
                        }
                        if(img.height * img.scaleY >= maxHeight){
                            img.scaleY = maxHeight / img.height;
                        }
                    }
                });
                img.on('moving',(e) => {
                    if(fixedInfo){
                        if(img.left <= fixedInfo.left || img.top <= fixedInfo.left){
                            img.setCoords();
                            img.left = Math.max(img.left, fixedInfo.left);
                            img.top = Math.max(img.top, fixedInfo.top);
                        }
                        let maxLeft = fixedInfo.left + fixedInfo.width - img.width * img.scaleX;
                        let maxTop = fixedInfo.top + fixedInfo.height - img.height * img.scaleY;
                        if(img.left >= maxLeft || img.top >= maxTop) {
                            img.setCoords();
                            img.left = Math.min(img.left, maxLeft);
                            img.top = Math.min(img.top, maxTop);
                        }
                    }
                });
c,文字的移动,放大缩小,旋转实现:

 
                txt.on('moved',(e) => {
                    if(e.target) {
                        let selectObj = this.props.templateModel.rows.filter(d => {return d.uid === e.target.uid})[0];
                        if(selectObj) {
                            selectObj.top = e.target.top;
                            selectObj.left = e.target.left;
                            this.UpdateItemByChild(selectObj);
                        }
                    }
                })
                txt.on('rotated',(e) => {
                    if(e.target) {
                        let selectObj = this.props.templateModel.rows.filter(d => {return d.uid === e.target.uid})[0];
                        if(selectObj) {
                            selectObj.angle = e.target.angle;
                            selectObj.width = e.target.width;
                            selectObj.height = e.target.height;
                            selectObj.top = e.target.top;
                            selectObj.left = e.target.left;
                            this.UpdateItemByChild(selectObj);
                        }
                    }
                })
                txt.on('scaled',(e) => {
                    if(e.target) {
                        let selectObj = this.props.templateModel.rows.filter(d => {return d.uid === e.target.uid})[0];
                        if(selectObj) {
                            if(e.target.scaleX) {
                                let tempFontSize = Math.ceil(e.target.fontSize * (e.target.scaleX / this.props.templateModel.scaleX));
                                e.target.fontSize = tempFontSize;
                                selectObj.fontSize = tempFontSize;
                                // X轴 Y轴是一样的缩放
                                e.target.scaleX = this.props.templateModel.scaleX;
                                selectObj.scaleX = this.props.templateModel.scaleX;
                                e.target.scaleY = this.props.templateModel.scaleY;
                                selectObj.scaleY = this.props.templateModel.scaleY;
                                selectObj.width = e.target.width;
                                selectObj.height = e.target.height;
                                selectObj.top = e.target.top;
                                selectObj.left = e.target.left;
                                this.UpdateItemByChild(selectObj);
                            }
                            this.UpdateItemByChild(selectObj);
                        }
                    }
                });
d,特别注意当文字大小改变时,字体大小需要随之变更

 
                txt.on('scaled',(e) => {
                    if(e.target) {
                        let selectObj = this.props.templateModel.rows.filter(d => {return d.uid === e.target.uid})[0];
                        if(selectObj) {
                            if(e.target.scaleX) {
                                let tempFontSize = Math.ceil(e.target.fontSize * (e.target.scaleX / this.props.templateModel.scaleX));
                                e.target.fontSize = tempFontSize;
                                selectObj.fontSize = tempFontSize;
                                // X轴 Y轴是一样的缩放
                                e.target.scaleX = this.props.templateModel.scaleX;
                                selectObj.scaleX = this.props.templateModel.scaleX;
                                e.target.scaleY = this.props.templateModel.scaleY;
                                selectObj.scaleY = this.props.templateModel.scaleY;
                                selectObj.width = e.target.width;
                                selectObj.height = e.target.height;
                                selectObj.top = e.target.top;
                                selectObj.left = e.target.left;
                                this.UpdateItemByChild(selectObj);
                            }
                            this.UpdateItemByChild(selectObj);
                        }
                    }
                });
后端关键代码:
a,ImageMagick 把 文字合并在原图上:
convert -font "H:\Works\Coordinator\Coordinator.MvcWebAPI\ImageMagick\simsun.ttc"
-fill #FE9200 -pointsize 450 -gravity northwest -annotate 28.0971789257395x28.0971789257395+4104.47275822051+437.86943092823 "文字定义"
"H:\Works\Coordinator\Coordinator.MvcWebAPI\01.SourceFiles\diy\20210317\140907767-2e86273b978445359c5cdd9be9beef91.jpg"
"H:\Works\Coordinator\Coordinator.MvcWebAPI\01.SourceFiles\diy\20210317\140907767-2e86273b978445359c5cdd9be9beef91.jpg"
b,ImageMagick 把附加图片合并在原图上:
convert "H:\Works\Coordinator\Coordinator.MvcWebAPI\01.SourceFiles\diy\20210317\140907767-2e86273b978445359c5cdd9be9beef91.jpg" ( -resize 1000x1000!
"https://localhost:44300/01.SourceFiles/diy/20210317/-44a34290d3424b55a04a1f59b03f8e0d.png" -background transparent -rotate 0 )
-gravity northwest -geometry +6798.3525+363.902921615202 -composite
"H:\Works\Coordinator\Coordinator.MvcWebAPI\01.SourceFiles\diy\20210317\140907767-2e86273b978445359c5cdd9be9beef91.jpg"
c,生成原图与预览图:
convert "H:\Works\Coordinator\Coordinator.MvcWebAPI\01.SourceFiles\diy\20210317\140907767-2e86273b978445359c5cdd9be9beef91.jpg"
-resize 1000x
"H:\Works\Coordinator\Coordinator.MvcWebAPI\01.SourceFiles\diy\20210317\140907767-2e86273b978445359c5cdd9be9beef91_s.jpg"
四,软件预览
有希望更深层次交流的朋友,请扫描博客头像的二维码

欢迎指正。
采用React + Fabric + ImageMagick 实现大图片DIY定制的更多相关文章
- Android相机使用(系统相机、自定义相机、大图片处理)
		本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显示出来,该例子也会涉及到Android加载大图片时候的处理(避免OOM),还有简要提一下有些人Surf ... 
- Android大图片裁剪终极解决方案 原理分析
		约几个月前,我正为公司的APP在Android手机上实现拍照截图而烦恼不已. 上网搜索,确实有不少的例子,大多都是抄来抄去,而且水平多半处于demo的样子,可以用来讲解知识点,但是一碰到实际项目,就漏 ... 
- .net项目中上传大图片失败
		.net项目中有时用户提出要上传大图片,一张图片有可能十几兆,本来用的第三方的上传控件,有限制图片上传大小的设置,以前设置的是2M.按照用户的要求,以为直接将限制图片上传大小的设置改下就可以了,但是当 ... 
- Android调用系统相机、自定义相机、处理大图片
		Android调用系统相机和自定义相机实例 本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显示出来,该例子也会涉及到Android加载大图片时候的处理 ... 
- jQuery实现等比例缩放大图片
		在布局页面时,有时会遇到大图片将页面容器“撑破”的情况,尤其是加载外链图片(通常是通过采集的外站的图片).那么本文将为您讲述使用jQuery如何按比例缩放大图片,让大图片自适应页面布局. 通常我们 ... 
- Android大图片裁剪终极解决方案(上:原理分析)
		转载声明:Ryan的博客文章欢迎您的转载,但在转载的同时,请注明文章的来源出处,不胜感激! :-) http://my.oschina.net/ryanhoo/blog/86842 约几个月前,我正 ... 
- 图片_ _Android有效解决加载大图片时内存溢出的问题 2
		Android有效解决加载大图片时内存溢出的问题 博客分类: Android Android游戏虚拟机算法JNI 尽量不要使用setImageBitmap或 setImageResource或 Bit ... 
- jQuery实现等比例缩放大图片让大图片自适应页面布局
		通常我们处理缩略图是使用后台代码(PHP..net.Java等)根据大图片生成一定尺寸的缩略图,来供前台页面调用,当然也有使用前台javascript脚本将加载后的大图强行缩放,变成所谓的缩略图,这种 ... 
- java快速获取大图片的分辨率(大图片格式JPG,tiff ,eg)
		问题描述:怎样快速获取一个20MB图片的分辨率? 程序代码: package test; import java.awt.Dimension; import java.awt.image.Buffer ... 
随机推荐
- vue component :is
			vue component :is Vue <component> element https://vuejs.org/v2/guide/components.html#Dynamic-C ... 
- React Big Changes All in One
			React Big Changes All in One React 重大更新 React Versions React 版本变更 https://reactjs.org/versions/ sema ... 
- LeetCode & linked list bug
			LeetCode & linked list bug add-two-numbers shit test /** * Definition for singly-linked list. * ... 
- how to using Linux pipe command output another command's help content to a file
			how to using Linux pipe command output another command's help content to a file Linux tee > >& ... 
- moment.js 时间格式转换
			moment.js 时间格式转换 moment.js 时间转化 bug 格式错误 bug 02:00 => 14:00 format HH 与 hh HH === 24 小时制 hh === 1 ... 
- NGK Global首尔站:内存是未来获取数字财富的新模式
			近日,NGK路演在NGK韩国社区的积极举办下顺利落下帷幕.此次路演在首尔举行,在活动当天,NGK的核心团队成员.行业专家.投资银行精英.生态产业代表和数百名NGK韩国社区粉丝一起参加NGK Globa ... 
- GridSearchCV网格搜索得到最佳超参数, 在K近邻算法中的应用
			最近在学习机器学习中的K近邻算法, KNeighborsClassifier 看似简单实则里面有很多的参数配置, 这些参数直接影响到预测的准确率. 很自然的问题就是如何找到最优参数配置? 这就需要用到 ... 
- Kubernetes和docker----1.开始使用k8s和docker
			开始使用Kubernetes和docker docker命令 运行一个容器 docker run busybox echo "Hello world" 构建容器镜像 docker ... 
- 手把手教你Spring Boot整合Mybatis Plus 代码生成器
			一.在pom.xml中添加所需依赖 <!-- MyBatis-Plus代码生成器--> <dependency> <groupId>com.baomidou< ... 
- @Transaction注解失效的几种场景
			一.@Transactional介绍 1.@Transactional注解可以作用于哪些地方? @Transactional 可以作用在接口.类.类方法上. 作用于类:表示所有该类的public方法都 ... 
