Gin + Element + 云IDE 挑战一小时打造春联生成平台

一、云空间创建

1、点击官方链接即可开始创建工作空间

2、查看已创建好的工作空间

二、云IDE使用

1、在 git 地址钱加入 ide 回车即可访问云IDE

2、可以看到 云IDE 的界面跟本地的 VSCODE IDE 界面是相同的

图-云IDE

图-本地 IDE 

3、云IDE 优点

云端预制了常见的开发环境,无需下载安装,一键创建项目,灵活配置代码仓和云主机。同时支持在线安装 VSCode 插件来增强使用体验,支持从基础组件快速构建高阶组件,无需重新编写组件,提高研发效率。随时随地开发编码,拥有媲美本地IDE 的流畅编码体验。

三、封装工具 

1、该工具用于生成指定春联,指定:words  内容,horV 方向,font 字体

2、原理是在图片上渲染字体像素

Golang 使用 image 库将图片转字符_余衫马的博客-CSDN博客Golang 使用 image 库将图片转字符https://blog.csdn.net/weixin_47560078/article/details/127480457?spm=1001.2014.3001.55013、实现代码

package util

import (
	"flag"
	"github.com/golang/freetype"
	"image"
	"image/color"
	"image/png"
	"io/ioutil"
	"log"
	. "mock/lib/font/config"
	"os"
)

// charset 中文文字
// horV - H-横排,V-竖排
// out 输出文件名
// font 字体

func Generator(charset []string, outPath string, horV string, textFont TextFont) {

	// 打开背景图
	bgFile, err := os.OpenFile("../img/bg.png", os.O_RDONLY, 6)
	if err != nil {
		log.Fatal(err)
	}

	defer bgFile.Close()

	// 解码
	i, err := png.Decode(bgFile)
	if err != nil {
		log.Fatal(err)
	}

	// 图片的宽度
	srcWidth := 640
	// 图片的高度
	srcHeight := 640

	if horV == "H" {
		srcWidth = 640 * len(charset)
	}

	if horV == "V" {
		srcHeight = 640 * len(charset)
	}

	imgFile, _ := os.Create(outPath)
	defer imgFile.Close()
	img := image.NewRGBA(image.Rect(0, 0, srcWidth, srcHeight))

	if horV == "H" {
		log.Println("横向生成,渲染背景...")
		// 根据字符长度创建背景
		for index := 0; index < len(charset); index++ {
			// 复制背景图
			for y := 0; y < srcHeight; y++ {
				for x := index * 640; x < srcWidth; x++ {
					if x < 640 {
						img.Set(x, y, i.At(x, y))
					} else {
						img.Set(x, y, i.At(x-index*640, y))
					}
				}
			}
		}
	}

	if horV == "V" {
		log.Println("竖向生成,渲染背景...")
		// 根据字符长度创建背景
		for index := 0; index < len(charset); index++ {
			// 复制背景图
			for y := index * 640; y < srcHeight; y++ {
				for x := 0; x < srcWidth; x++ {
					if y < 640 {
						img.Set(x, y, i.At(x, y))
					} else {
						img.Set(x, y, i.At(x, y-index*640))
					}
				}
			}
		}
	}

	// 读取字体数据
	fontFileName := "../lib/font/" + textFont.FontName
	fontBytes, err := ioutil.ReadFile(fontFileName)
	log.Printf("加载字体:%s...", textFont.FontName)
	if err != nil {
		log.Fatal(err)
	}

	// 载入字体数据
	font, err := freetype.ParseFont(fontBytes)
	if err != nil {
		log.Println("载入字体失败!", err)
	}
	f := freetype.NewContext()
	// 设置分辨率
	f.SetDPI(100)
	// 设置字体
	f.SetFont(font)
	// 设置尺寸
	f.SetFontSize(textFont.FontSize)
	f.SetClip(img.Bounds())
	// 设置输出的图片
	f.SetDst(img)
	// 设置字体颜色(黑色)
	f.SetSrc(image.NewUniform(color.Black))

	if horV == "H" {
		log.Println("开始绘制对联...")
		// 绘制字符
		for index := 0; index < len(charset); index++ {
			// 设置字体的位置
			pt := freetype.Pt(textFont.X+index*640, textFont.Y)

			_, err = f.DrawString(charset[index], pt)
			if err != nil {
				log.Fatal(err)
			}
		}
	}

	if horV == "V" {
		// 绘制字符
		for index := 0; index < len(charset); index++ {
			// 设置字体的位置
			pt := freetype.Pt(textFont.X, textFont.Y+index*640)

			_, err = f.DrawString(charset[index], pt)
			if err != nil {
				log.Fatal(err)
			}
		}
	}

	// 以png 格式写入文件作为输出
	err = png.Encode(imgFile, img)
	if err != nil {
		log.Fatal(err)
	}
	log.Println("Done.")
}

四、Template 引用 Element UI

1、参考 Element 官方文档

2、页面功能:用户在输入框填写文本,选择效果(方向,字体)后,点击 Run 按钮,返回用户预期效果图片

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
    <!-- import CSS -->
    <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
</head>

<body>
<div id="app">
    <div class="demo-image__lazy" style="margin-top:30px;">
        <el-image src="../res/3f8b879b-fa29-4dda-8e08-1f82cbd2d895.png"></el-image>
    </div>
    <el-input placeholder="请输入内容" v-model="input" clearable>
    </el-input>
    <el-radio v-model="horV" label="H">水平方向</el-radio>
    <el-radio v-model="horV" label="V">垂直方向</el-radio>
    <el-radio v-model="font" label="font1">字体1</el-radio>
    <el-radio v-model="font" label="font2">字体2</el-radio>
    <el-button style="margin-left: 10px;margin-top:30px;" size="small" type="success" @click="submitUpload"
               v-loading.fullscreen.lock="fullscreenLoading" element-loading-text="拼命加载中">Run
    </el-button>
    <div class="demo-image__lazy" style="margin-top:30px;">
        <el-image v-for="url in urls" :key="url" :src="url" lazy></el-image>
    </div>
</div>
</body>
<!-- import Vue before Element -->
<script src="https://unpkg.com/vue@2/dist/vue.js"></script>
<!-- import JavaScript -->
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<script>
    new Vue({
        el: '#app',
        data() {
            return {
                horV: 'H',
                font: 'font1',
                input: '',
                urls: [],
                fullscreenLoading: false
            };
        },
        methods: {
            // 提交表单
            submitUpload() {
                if (this.input.length > 0) {
                    console.log(this.input, this.horV, this.font);
                    let formdata = {
                        words: this.input,
                        horV: this.horV,
                        font: this.font,
                    };
                    this.fullscreenLoading = true;
                    var that = this;
                    axios({
                        url: '/api/GetImg',
                        method: 'post',
                        data: formdata,
                        headers: {
                            'Content-Type': 'application/x-www-form-urlencoded'
                        }
                    })
                        .then(function (response) {
                            console.log('success');
                            console.log(response);
                            that.urls = response.data.urls;
                            that.fullscreenLoading = false;
                        })
                        .catch(function (error) {
                            console.log('error');
                            console.log(error);
                        });
                }
            }
        }
    })
</script>

</html>

五、定义控制器

1、Index 用于返回定义好的 Index.tmpl 模板

2、GetImg 用于响应页面请求

package controller

import (
	"net/http"

	"github.com/gin-gonic/gin"

	"mock/lib/font/config"
	. "mock/util"

	uuid "github.com/satori/go.uuid"
)

// 首页
func Index(c *gin.Context) {
	c.HTML(http.StatusOK, "index.tmpl", nil)
}

// 参数 worlds,horV,font => 春联内容,方向,字体
func GetImg(c *gin.Context) {

	// ================== 参数处理
	words := c.PostForm("words")
	// 处理字符
	var charset []string
	if len(words) > 0 {
		for _, v := range words {
			charset = append(charset, string(v))
		}
	} else {
		charset = []string{"你", "好", "世", "界"}
	}

	// 处理方向
	horV := c.PostForm("horV")
	if len(horV) > 0 {
		if horV != "H" && horV != "V" {
			horV = "H"
		}
	} else {
		horV = "H"
	}

	// 输出路径
	outPath := "../res/" + uuid.NewV4().String() + ".png"

	// 处理字体
	font := config.Font_1
	ft := c.PostForm("font")
	if len(ft) > 0 {
		if ft != "font1" && ft != "font2" {
			font = config.Font_1
		}
	} else {
		font = config.Font_1
	}

	// 生成对联
	Generator(charset, outPath, horV, font)

	// 返回数组
	urls := []string{outPath}

	c.JSON(http.StatusOK, gin.H{
		"status": "success",
		"urls":   urls,
	})
}

六、跨域中间件

1、如果不做跨域配置,axios 请求可能会被拦截

package middleware
import (
	"github.com/gin-contrib/cors"
	"github.com/gin-gonic/gin"
	"time"
	)
	
	// 跨域请求
	
	func Cors() gin.HandlerFunc {
		handlerFunc := cors.New(cors.Config{
			AllowMethods:     []string{"*"},
			AllowHeaders:     []string{"content-type", "token", "fileType", "size", "digest"}, //此处设置非默认之外的请求头(自定义请求头),否则会出现跨域问题
			AllowAllOrigins:  true,
			AllowCredentials: true,
			MaxAge:           24 * time.Hour,
			ExposeHeaders:    []string{"*"},
		})
		return handlerFunc
	}
	
	// gin 上下文配置 cors
	
	func CORSMiddleware() gin.HandlerFunc {
		return func(c *gin.Context) {
			c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
			c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
			c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
			c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, GET, PUT, HEAD, OPTIONS")
	
			if c.Request.Method == "OPTIONS" {
				c.AbortWithStatus(204)
				return
			}
	
			c.Next()
		}
	}
	
	//nginx 跨域
	
	//server {
	//#以上省略
	//add_header 'Access-Control-Allow-Origin' '*';
	//add_header 'Access-Control-Allow-Headers' 'X-Pagination-Current-Page,Content-Type';
	//add_header 'Access-Control-Allow-Methods' 'PUT,GET,POST,HEAD,DELETE';
	//add_header 'Access-Control-Expose-Headers' 'X-Pagination-Current-Page,Content-Type';
	//#以下省略
	//}
	//Allow-Headers "Accept","Accept-Encoding","Host","Origin","Referer","User-Agent",

七、定义路由

1、放行静态路径和页面模板

2、放行业务接口

package router

import (
	. "mock/controller"
	"mock/middleware"
	"net/http"

	"github.com/gin-gonic/gin"
)

/*
InitRouter 路由初始化
*/
func InitRouter() *gin.Engine {
	router := gin.Default()
	// 加载 templates 文件夹下所有的 tmpl
	router.LoadHTMLGlob("../templates/*")
	router.StaticFS("/res", http.Dir("../res"))
	router.GET("/", Index)
	v1 := router.Group("/api")
	v1.Use(middleware.CORSMiddleware())
	{
		v1.Any("/GetImg", GetImg)
	}
	return router
}

八、main入口

1、以 8080 端口启动服务,默认为 localhost

package main

import (
	"mock/router"
)

func main() {

	r := router.InitRouter()
	r.Run(":8080")

}

九、自动化脚本

1、先执行 go build 生成可执行文件 cmd 后,编写自动执行脚本

autoOpen: true
apps:
  - port: 8080
    command: go env -w GOPROXY=https://goproxy.cn,direct & cd cmd && ./cmd
    root: ./
    name: demo
    description: demo
    autoOpen: true

十、运行效果

1、重新打开页面时,脚本自动执行,自动开启前后端

十一、云IDE测评总结 

1、编码流畅度:目前使用来说很流畅,偶尔会有字符输入延时,也有出现整个IDE卡死、没有提示语法的情况

2、开发环境:云端自带的开发环境能满足大部分的需求,也可以自行安装插件,我使用的是Go环境也能自动识别,这点可以节省一大部分搭建环境的时间(下载-安装-配置),提高开发效率。

3、灵活度:不局限与本地机器,只要有网络,随时随地都可以使用云IDE。

猜你喜欢

转载自blog.csdn.net/weixin_47560078/article/details/127712877