原创,转载请注明出处!

最开始用图形来模仿文字进行各种角度的倒立和排列,后来切换为文字后,有很多问题。总结如下:

1、程序在画图形和画文字方面不一样,图形的是从原点开始(0,0),而文字则从文字的基线开始(0,baseline)

2、在增加角度偏移时,文字或图形的高宽会产生变化(偏∠45度时达到最大),这时候为了让它们顶点对齐,需要计算偏移量(用三角函数)

3、在绘图时,会先旋转“画布”(描述可能不准确),再绘制文字。此时要往回旋转,否则下一个图形会顺着这个角度继续画。

4、为了让图形保持固定宽度,对于有偏角的文字,需要平均缩小左右间距(否则不同的角度,固定的文字个数,会让图形宽度不同)

效果图:

源码:(代码还可以再整理和优化,但限于计划时间,懒得弄了)

package mainimport ( "math""reflect""time""math/rand""github.com/golang/freetype/truetype""github.com/fogleman/gg" // 需要安装这个包"flag" "fmt" "io/ioutil" "log"
)var wORDSLIB = []interface{}{"赵","钱","孙","李","周","吴","郑","冯","陈","褚","卫","蒋","沈","韩","杨","朱","秦","尤","许","何","吕","施","张","孔","曹","严","华","金","魏","陶","姜","戚","谢","邹","喻","柏","水","窦","章","云","苏","潘","葛","奚","范","彭","郎","鲁","韦","昌","马","苗","凤","花","方","俞","任","袁","柳","酆","鲍","史","唐","费","廉","岑","薛","雷","贺","倪","汤","滕","殷","罗","毕","郝","邬","安","常","乐","于","时","傅","皮","卞","齐","康","伍","余","元","卜","顾","孟","平","黄","和","穆","萧","尹","姚","邵","湛","汪","祁","毛","禹","狄","米","贝","明","臧","计","伏","成","戴","谈","宋","茅","庞","熊","纪","舒","屈","项","祝","董","梁","杜","阮","蓝","闵","席","季","麻","强","贾","路","娄","危","江","童","颜","郭","梅","盛","林","刁","钟","徐","邱","骆","高","夏","蔡","樊","胡","凌","霍","虞","万","支","柯","昝","管","卢","莫","经","房","裘","缪","干","解","应","宗","丁","宣","贲","邓","郁","单","杭","洪","包","诸","左","石","崔","吉","钮","龚","程","嵇","邢","滑","裴","陆","荣","翁","荀","羊","於","惠","甄","曲","家","封","芮","羿","储","靳","汲","邴","糜","松","井","段","富","巫","乌","焦","巴","弓","牧","隗","山","谷","车","侯","宓","蓬","全","郗","班","仰","秋","仲","伊","宫","宁","仇","栾","暴","甘","钭","厉","戎","祖","武","符","刘","景","詹","束","龙","叶","幸","司","韶","郜","黎","蓟","薄","印","宿","白","怀","蒲","邰","从","鄂","索","咸","籍","赖","卓","蔺","屠","蒙","池","乔","阴","鬱","胥","能","苍","双","闻","莘","党","翟","谭","贡","劳","逄","姬","扶","堵","冉","宰","郦","雍","卻","璩","桑","桂","濮","牛","寿","通","边","扈","燕","冀","郏","浦","尚","农","温","别","庄","晏","柴","瞿","阎","充","慕","连","茹","习","宦","艾","鱼","容","向","古","易","慎","戈","廖","庾","终","暨","居","衡","步","都","耿","满","弘","匡","国","文","寇","广","禄","阙","东","欧","殳","沃","利","蔚","越","夔","隆","师","巩","厍","聂","晁","勾","敖","融","冷","訾","辛","阚","那","简","饶","空","曾","毋","沙","乜","养","鞠","须","丰","巢","关","蒯","相","查","后","荆","红","游","竺","权","逯","盖","益","桓","公","俟","上","官","阳","人","赫","皇","甫","尉","迟","澹","台","冶","政","淳","太","叔","申","轩","辕","令","狐","离","宇","长","鲜","闾","丘","徒","仉","督","子","颛","端","木","西","漆","雕","正","壤","驷","良","拓","跋","夹","父","晋","楚","闫","法","汝","鄢","涂","钦","百","里","南","门","呼","延","归","海","舌","微","生","岳","帅","缑","亢","况","郈","有","琴","商","牟","佘","佴","伯","赏","墨","哈","谯","笪","年","爱","佟","第","五","言","福","姓"}var ( dpi      = flag.Float64("dpi", 72, "screen resolution in Dots Per Inch") fontfile = flag.String("fontfile", "./SIMYOU.TTF", "filename of the ttf font") size     = flag.Float64("size", 40, "font size in points")
)func main() { x := "abc"fmt.Println(x[0:3])drawImageBygg()
} func drawImageBygg(){dc := gg.NewContext(320, 56)  // 56 => w*sin(45) + h*sin(45)  45度时,字体达到最大高度dc.SetRGBA(1, 1, 1,0) // 设置背景色:末尾为透明度 1-0(1-不透明 0-透明)dc.Clear()dc.SetRGBA(0, 0, 0,1) // 设置字体色fontBytes, err := ioutil.ReadFile(*fontfile) if err != nil { log.Println(err) return } font, err := truetype.Parse(fontBytes)if err != nil {log.Println(err) return }face := truetype.NewFace(font, &truetype.Options{Size: *size,DPI:*dpi,}) dc.SetFontFace(face)// 初始化用于计算坐标的变量fm := face.Metrics()ascent := float64(fm.Ascent.Round()) // 字体的基线到顶部距离decent := float64(fm.Descent.Round()) // 字体的基线到底部的距离w := float64(fm.Height.Round()) // 方块字,大多数应为等宽字,即和高度一样h := float64(fm.Height.Round())totalWidth := 0.0 // 目前已累积的图片宽度(需要用来计算字体位置)// 随机取汉字,定位倒立的字words := getRandomMembersFromMemberLibary(wORDSLIB,8) // 取8个字reverseWordsIndex := getRandomMembersFromMemberLibary([]interface{}{0,1,2,3,4,5,6,7},2) // 随机2个倒立字for i,word := range words{degree := If(Contain(i,reverseWordsIndex),float64(RandInt64(150,210)),float64(RandInt64(-30,30))) // 随机角度,正向角度 -30~30,倒立角度 150~210x,y,leftCutSize,rightCS := getCoordByQuadrantAndDegree(w,h,ascent,decent,degree,totalWidth)dc.RotateAbout(gg.Radians(degree),0,0)dc.DrawStringAnchored(word.(string), x,y, 0,0)dc.RotateAbout(-1*gg.Radians(degree),0,0)totalWidth = totalWidth + leftCutSize + rightCSfmt.Println("x:",x,"y:",y,"total:",totalWidth,"degree:",degree)}dc.Stroke()dc.SavePNG("out.png")fmt.Println("Wrote out.png OK.")
}func getCoordByQuadrantAndDegree(w,h,ascent,descent,degree,beforTotalWidth float64)(x,y,leftCutSize,rightCutSize float64){var totalWidth float64switch{case degree<=0 && degree >= -40:  // 第一象限:逆时针 -30度 ~ 0  <=>  330 ~ 360 (目前参数要传入负数)  rd := -1 * degree // 转为正整数,便于计算leftCutSize = w*getDegreeSin(90-rd)rightCutSize = h*getDegreeSin(rd)offset := (leftCutSize + rightCutSize - w) / 2 // 横向偏移量(角度倾斜越厉害,占宽越多,通过偏移量分摊给它的左右边距来收窄)leftCutSize,rightCutSize = leftCutSize - offset,rightCutSize - offsettotalWidth = beforTotalWidth + leftCutSize x = getDegreeSin(90 - rd)*totalWidth - w y = ascent + getDegreeSin(rd)*totalWidth  case degree >=0 && degree <= 40:  // 第四象限:顺时针 0 ~ 30度leftCutSize = h*getDegreeSin(degree)rightCutSize = w*getDegreeSin(90-degree)offset := (leftCutSize + rightCutSize - w) / 2leftCutSize,rightCutSize = leftCutSize - offset,rightCutSize - offsettotalWidth = beforTotalWidth + leftCutSize // 现在totalwidth = 前面的宽 + 自己的左切边x = getDegreeSin(90-degree)*totalWidthy = ascent - getDegreeSin(degree)*totalWidth case degree >= 180 && degree <= 220:  // 第二象限:顺时针 180 ~ 210度rd := degree - 180leftCutSize = h*getDegreeSin(rd)rightCutSize = w*getDegreeSin(90-rd)offset := (leftCutSize + rightCutSize - w) / 2 leftCutSize,rightCutSize = leftCutSize - offset,rightCutSize - offsettotalWidth = beforTotalWidth + leftCutSize x = -1 * (getDegreeSin(90-rd)*totalWidth + w)y = getDegreeSin(rd)*totalWidth - descentcase degree >= 140 && degree <= 180:  // 第三象限:顺时针 150 ~ 180度rd := 180-degreeleftCutSize = w*getDegreeSin(90-rd)rightCutSize = h*getDegreeSin(rd)offset := (leftCutSize + rightCutSize - w) / 2leftCutSize,rightCutSize = leftCutSize - offset,rightCutSize - offsettotalWidth = beforTotalWidth + leftCutSize x = -1 * (getDegreeSin(90-rd) * totalWidth)y = -1 * (getDegreeSin(rd) * totalWidth + descent)default: panic(fmt.Sprintf("非法的参数:%f",degree))}return
}func getDegreeSin(degree float64) float64{return math.Sin(degree*math.Pi/180)
}//RandInt64 ...
func RandInt64(min, max int64) int64 {if min >= max || min == 0 || max == 0 {return max}rand.Seed(time.Now().UnixNano())return rand.Int63n(max-min) + min
}func getRandomMembersFromMemberLibary(lib []interface{},size int)[]interface{}{source,result := make([]interface{},0),make([]interface{},0)if size <= 0 || len(lib) == 0{return result}for _,v := range lib{source = append(source,v)}if size >= len(lib){return source}for i:=0;i<size;i++{rand.Seed(time.Now().UnixNano())pos := rand.Intn(len(source))result = append(result,source[pos])source = append(source[:pos], source[pos+1:]...)}return result
}//Contain ...
func Contain(obj interface{}, target interface{}) bool {targetValue := reflect.ValueOf(target)switch reflect.TypeOf(target).Kind() {case reflect.Slice, reflect.Array:for i := 0; i < targetValue.Len(); i++ {if targetValue.Index(i).Interface() == obj {return true}}case reflect.Map:if targetValue.MapIndex(reflect.ValueOf(obj)).IsValid() {return true}}return false
}//If ...
func If(expr bool,trueVal float64,falseVal float64) float64{if expr {return trueVal }return falseVal
}

  

需要一个字体文件,这里使用的幼圆(幼圆免费,其他免费字体懒得找)。

注意:字体不同,宽度和高度可能会需要调整。

转载于:https://www.cnblogs.com/Denny_Yang/p/9317538.html

golang 防知乎 中文验证码 源码相关推荐

  1. thinkphp整合极验滑动验证码源码演示下载

    thinkphp整合极验滑动验证码源码演示下载-二当家的php源码下载 <!DOCTYPE html><html lang="en"><head> ...

  2. Java编程思想+Effective Java+Java核心技术+Java核心技术 卷II+Java语言程序设计(中文+英文+源码)

    Java四大名著(中文+英文+源码 ) 传说中的java四大名著,分享出来方便大家学习! 书名如下: Java编程思想 Effective Java(第2版) Java核心技术 卷I(第8版) Jav ...

  3. 基于java安防管理平台计算机毕业设计源码+系统+lw文档+mysql数据库+调试部署

    基于java安防管理平台计算机毕业设计源码+系统+lw文档+mysql数据库+调试部署 基于java安防管理平台计算机毕业设计源码+系统+lw文档+mysql数据库+调试部署 本源码技术栈: 项目架构 ...

  4. 基于Java毕业设计新型冠状病毒防控咨询网站2020源码+系统+mysql+lw文档+部署软件

    基于Java毕业设计新型冠状病毒防控咨询网站2020源码+系统+mysql+lw文档+部署软件 基于Java毕业设计新型冠状病毒防控咨询网站2020源码+系统+mysql+lw文档+部署软件 本源码技 ...

  5. Qt编写安防楼宇对讲管理平台源码

    Qt编写安防楼宇对讲管理平台源码 同时集成了楼宇对讲.住户报警.门禁控制.公共报警.视频监控等模块. 系统管理部分包括系统配置.对讲配置.住户配置.公共配置.监控配置.地图管理.视频联动.用户管理.区 ...

  6. PHP防伪防串货溯源系统源码

    PHP开发的防伪防串货溯源系统源码,简洁美观的风格,使用步骤简单,进入溯源公共平台.输入产品溯源码.查看溯源信息,输入要查询的防伪码(溯源码)内容.就会自动溯源或者辨别真假. 下载地址:www.suc ...

  7. 基于java社区疫情防控系统计算机毕业设计源码+系统+lw文档+mysql数据库+调试部署

    基于java社区疫情防控系统计算机毕业设计源码+系统+lw文档+mysql数据库+调试部署 基于java社区疫情防控系统计算机毕业设计源码+系统+lw文档+mysql数据库+调试部署 本源码技术栈: ...

  8. 计算机毕业设计Java疫情防控物业管理系统(系统+源码+mysql数据库+Lw文档)

    计算机毕业设计Java疫情防控物业管理系统(系统+源码+mysql数据库+Lw文档) 计算机毕业设计Java疫情防控物业管理系统(系统+源码+mysql数据库+Lw文档) 本源码技术栈: 项目架构:B ...

  9. Go (Golang) 工具之copyright 添加 | go源码添加授权头

    文章目录 Go (Golang) 工具之copyright 添加 | go源码添加版权 什么是addlicense addlicense安装和使用 Go (Golang) 工具之copyright 添 ...

最新文章

  1. JSP_include指令和lt;jsp:includegt;
  2. 某CMSV1.0代码审计
  3. neo4j CQL语句
  4. coolite TreePanel CheckBox联动
  5. J2EE应用与移动互联网-写在前头
  6. 描写计算机老师上课的神态,请你用一段话描写一位老师上课的情景,注意抓住神态语言动作等细节...
  7. 使用Python和Prometheus跟踪天气
  8. 游戏教玩家学java,技术|帮你学习Java语言的游戏
  9. VB 二进制文件的操作
  10. 计算机丢失boost,Win7系统安装后出现无法开启readyboost怎么办?
  11. 风尚云网学习篇-介绍chrome 浏览器的几个版本
  12. 《Nature》《Science》封面发表的AI相关文章
  13. dede后台登陆提示 验证码不正确 解决办法(新版)
  14. c 语言 sqlite,SQLite 的 C 语言编程
  15. 使用 Java 进行 HL7 编程
  16. 我爱淘冲刺阶段站立会议2每天任务2
  17. 情商 智商 逆商,哪个最重要?
  18. 微信服务号如何创建一个带参数的微信二维码?
  19. imx6 linux 开发环境,米尔iMX6UL开发板Linux操作系统的环境部署
  20. K-Means聚类算法原理及其python和matlab实现

热门文章

  1. 解决Jenkins邮件配置问题
  2. Michael-Scott非阻塞队列(lock-free)算法的C实现
  3. jQueryMobile引入文件后样式无法正常显示
  4. js基础练习---图片无缝左右滚动效果(主要以复制删除为主)
  5. 为什么我们不要 .NET 程序员
  6. mmap直接操作底层,相当于驱动
  7. tslib 编译移植步骤
  8. 述职答辩提问环节一般可以问些什么_每个优秀的人都会被看到述职晋升季buff大推送...
  9. mac mini mysql 安装_怎么在MacMini上安装ubuntu,Mac OS X 讨论区
  10. cnn 一维时序数据_一时序数据输入维cnn