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);
                        }
                    }
                });

View Code

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);
                        }
                    }
                });

View Code

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);
                        }
                    }
                });

View Code

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);
                        }
                    }
                });

View Code

 后端关键代码:

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" 

四,软件预览

有希望更深层次交流的朋友,请扫描博客头像的二维码

 

 欢迎指正。

给TA买糖
共{{data.count}}人
人已赞赏
经验教程

【.NET 与树莓派】小风扇模块

2021-3-17 11:53:00

经验教程

官方正式发布 Java 16

2021-3-17 14:54:00

⚠️
免责声明:根据《计算机软件保护条例》第十七条规定“为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬。”您需知晓本站所有内容资源均来源于网络,仅供用户交流学习与研究使用,版权归属原版权方所有,版权争议与本站无关,用户本人下载后不能用作商业或非法用途,需在24个小时之内从您的电脑中彻底删除上述内容,否则后果均由用户承担责任;如果您访问和下载此文件,表示您同意只将此文件用于参考、学习而非其他用途,否则一切后果请您自行承担,如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。 本站为个人博客非盈利性站点,所有软件信息均来自网络,所有资源仅供学习参考研究目的,并不贩卖软件,不存在任何商业目的及用途,网站会员捐赠是您喜欢本站而产生的赞助支持行为,仅为维持服务器的开支与维护,全凭自愿无任何强求。本站部份代码及教程来源于互联网,仅供网友学习交流,若您喜欢本文可附上原文链接随意转载。
无意侵害您的权益,请发送邮件至 momeis6@qq.com 或点击右侧 私信:momeis 反馈,我们将尽快处理。
0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
今日签到
有新私信 私信列表
搜索