一,需求背景:

某个印刷公司,有一系列的设计文件模板。接到客户订单时,就在这些设计文件模板上,做一些简单的定制,就能够满足客户的印刷需求。 如在设计文件模板上添加客户的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定制的更多相关文章

  1. Android相机使用(系统相机、自定义相机、大图片处理)

    本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显示出来,该例子也会涉及到Android加载大图片时候的处理(避免OOM),还有简要提一下有些人Surf ...

  2. Android大图片裁剪终极解决方案 原理分析

    约几个月前,我正为公司的APP在Android手机上实现拍照截图而烦恼不已. 上网搜索,确实有不少的例子,大多都是抄来抄去,而且水平多半处于demo的样子,可以用来讲解知识点,但是一碰到实际项目,就漏 ...

  3. .net项目中上传大图片失败

    .net项目中有时用户提出要上传大图片,一张图片有可能十几兆,本来用的第三方的上传控件,有限制图片上传大小的设置,以前设置的是2M.按照用户的要求,以为直接将限制图片上传大小的设置改下就可以了,但是当 ...

  4. Android调用系统相机、自定义相机、处理大图片

    Android调用系统相机和自定义相机实例 本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显示出来,该例子也会涉及到Android加载大图片时候的处理 ...

  5. jQuery实现等比例缩放大图片

      在布局页面时,有时会遇到大图片将页面容器“撑破”的情况,尤其是加载外链图片(通常是通过采集的外站的图片).那么本文将为您讲述使用jQuery如何按比例缩放大图片,让大图片自适应页面布局. 通常我们 ...

  6. Android大图片裁剪终极解决方案(上:原理分析)

    转载声明:Ryan的博客文章欢迎您的转载,但在转载的同时,请注明文章的来源出处,不胜感激! :-)  http://my.oschina.net/ryanhoo/blog/86842 约几个月前,我正 ...

  7. 图片_ _Android有效解决加载大图片时内存溢出的问题 2

    Android有效解决加载大图片时内存溢出的问题 博客分类: Android Android游戏虚拟机算法JNI 尽量不要使用setImageBitmap或 setImageResource或 Bit ...

  8. jQuery实现等比例缩放大图片让大图片自适应页面布局

    通常我们处理缩略图是使用后台代码(PHP..net.Java等)根据大图片生成一定尺寸的缩略图,来供前台页面调用,当然也有使用前台javascript脚本将加载后的大图强行缩放,变成所谓的缩略图,这种 ...

  9. java快速获取大图片的分辨率(大图片格式JPG,tiff ,eg)

    问题描述:怎样快速获取一个20MB图片的分辨率? 程序代码: package test; import java.awt.Dimension; import java.awt.image.Buffer ...

随机推荐

  1. Promise thenable All In One

    Promise thenable All In One Promise thenable 是指一个函数或一个对象的里面定义了一个 then 方法 Promises/A+ https://promise ...

  2. ECharts Pie All In One

    ECharts Pie All In One 饼图 https://echarts.apache.org/examples/zh/index.html#chart-type-pie 嵌套饼图 http ...

  3. React render twice bug

    React render twice bug React bug constructor render twice bug update render twice bug StrictMode htt ...

  4. taro table component

    taro table component https://juejin.im/post/5d901696f265da5b926bbcaa https://taro-ext.jd.com/search? ...

  5. API protocols All In One

    API protocols All In One SOAP vs. REST vs. JSON-RPC vs. gRPC vs. GraphQL vs. Thrift https://www.mert ...

  6. 炒币亏损一万美金?不如抢SPC空投!

    币圈的市场可以用风云变幻来形容,1月9日的时候比特币震荡,其他币种争先上涨,连平时都不涨的币种都拉出了10%-20%的领先涨幅,市场惊呼牛市来了,但喜悦还没有维持一天,1月10日(昨天)市场就走向另一 ...

  7. spring5学习笔记

    Spring5 框架概述 1.Spring 是轻量级的开源的 JavaEE 框架 2.Spring 可以解决企业应用开发的复杂性 3.Spring 有两个核心部分:IOC 和 Aop (1)IOC:控 ...

  8. 08.手写KNN算法测试

    导入库 import numpy as np from sklearn import datasets import matplotlib.pyplot as plt 导入数据 iris = data ...

  9. 12_MySQL如何对查询结果进行排序

    本节所涉及的sql语句: -- 排序关键字 SELECT empno,ename,hiredate FROM t_emp ORDER BY hiredate DESC; -- 排序字段相同的情况 SE ...

  10. hadoop支持lzo完整过程

    简介 启用lzo 启用lzo的压缩方式对于小规模集群是很有用处,压缩比率大概能降到原始日志大小的1/3.同时解压缩的速度也比较快. 安装lzo lzo并不是linux系统原生支持,所以需要下载安装软件 ...