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" 

四,軟件預覽

有希望更深層次交流的朋友,請掃描博客頭像的二維碼

 

 歡迎指正。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM