Go语言(Gin框架)实现传统文化读书软件
一、用GO语言实现
二、源代码公开,欢迎修改转载使用。
1. https://pan.baidu.com/s/1NW_Am0WsjwrTtcfZKVTkOg 提取码: ptaw
2. 下载地址:https://files.cnblogs.com/files/javatiandi/DuRenZhou.rar
3. 演示地址: http://106.12.161.76:8089/boat/index
部分源代码如下:
package main import ( "DuRenZhou/src/conf" "DuRenZhou/src/controllers" "github.com/gin-gonic/gin" "net/http" "strconv" ) func main() { router := gin.Default() router.StaticFS("/static", http.Dir("src/static")) router.LoadHTMLGlob("src/views/book/*") router.NoRoute(controllers.Handle404) router.StaticFile("/favicon.ico", "src/static/favicon/favicon.ico") //渡人舟 boat := router.Group("/boat") { boat.GET("/index", controllers.BoatBookMainIndex) boat.POST("/clientInit", controllers.BoatClientInit) boat.POST("/client", controllers.BoatClient) boat.POST("/mark", controllers.BoatBookMark) } sererPort := conf.AppConf.ServerConfig.Port port := strconv.Itoa(sererPort) router.Run(":" + port) }
package controllers import ( "DuRenZhou/src/conf" "fmt" "github.com/axgle/mahonia" "github.com/gin-gonic/gin" "io" "io/ioutil" "log" "math" "net/http" "os" "strconv" "strings" "sync" "time" "unicode/utf8" ) type PageContent struct { BookId string BookName string BookRealPath string PWordsCount int PContent string ClientWidth int ClientHeight int FontSize int OriginalX int OriginalY int PageHeight int LineSize int MaxLetters int PageNum int TotalPage int JumpMark string ReadType int JumpPageNum int } type BookDetail struct { conf.Book JumpMark string JumpPageNum int ReadType int } var once sync.Once var contentMap = make(map[string]string, 100) var bookList = make([]conf.Book, 0) func init() { once.Do(func() { initResources() }) } func GetResourcesPath() (string, error) { getwd, err := os.Getwd() if err != nil { panic("初始化资源路径出错!") os.Exit(2) } resources := getwd + "\\src\\resources" //fmt.Println(resources) return resources, err } func listResources() []string { list := make([]string, 0) resourcesPath, err := GetResourcesPath() if err != nil { panic("初始化资源路径出错!") os.Exit(2) } dir, err := ioutil.ReadDir(resourcesPath) if err != nil { panic("初始化资源路径出错!") os.Exit(2) } for _, file := range dir { if file.IsDir() { continue } else { if strings.Contains(file.Name(), "jumpMark") { continue } else { list = append(list, file.Name()) } } } return list } func initResources() { resourcesPath, err := GetResourcesPath() if err != nil { panic("初始化资源路径出错!") os.Exit(2) } resources := listResources() list := conf.AppConf.BookConfig.BookList for _, book := range list { for _, name := range resources { fmt.Println("书名:", name) if name == (book.BookName + ".txt") { book.RealPath = resourcesPath + "\\" + name fmt.Printf("书籍名称:%+v \n", book) bookList = append(bookList, book) } } } } func calcFontSize(deviceWidth int) int { /** 321px <= device-width <= 375px,font-size:11px ---> .item的width:34px 376px <= device-width <= 414px,font-size:12px ---> .item的width:37.4px 415px <= device-width <= 639px,font-size:15px ---> .item的width:40.8px 640px <= device-width <= 719px,font-size:20px ---> .item的width:51px 720px <= device-width <= 749px,font-size:22.5px ---> .item的width:76.5px 750px <= device-width <= 799px,font-size:23.5px ---> .item的width:79.8999999px 800px <= device-width ,font-size:25px ---> .item的width:85px */ fontSize := 25 switch { case deviceWidth <= 375: fontSize = 11 break case deviceWidth <= 414: fontSize = 12 break case deviceWidth <= 639: fontSize = 15 break case deviceWidth <= 719: fontSize = 20 break case deviceWidth <= 749: fontSize = 22 break case deviceWidth <= 799: fontSize = 23 break case deviceWidth > 799: fontSize = 25 break default: fontSize = 25 } fmt.Println(fontSize) return fontSize } func calcLineSize(pageHeight int, fontSize int) int { lineSize := 0 tmp1 := float64(fontSize) * 1.5 tmp2 := float64(pageHeight) / tmp1 lineSize = int(math.Floor(tmp2)) return lineSize } func BoatBookMainIndex(c *gin.Context) { list := bookList bookDetailList := make([]BookDetail, 0) for _, d := range list { //fmt.Println(d) bookDetail := BookDetail{} bookDetail.BookId = d.BookId bookDetail.BookName = d.BookName bookDetail.RealPath = d.RealPath //加载书签文件 jumpMarkPath := strings.ReplaceAll(bookDetail.RealPath, ".txt", "_jumpMark.txt") b := exists(jumpMarkPath) if b { jumpMarkGBK := ReadFile(jumpMarkPath) dec := mahonia.NewDecoder("gbk") jumpMarkUTF8 := dec.ConvertString(jumpMarkGBK) // fmt.Println("jumpMarkUTF8:", jumpMarkUTF8) bookDetail.JumpMark = jumpMarkUTF8 } bookDetailList = append(bookDetailList, bookDetail) } data := make(map[string][]BookDetail, 1) data["bookDetailList"] = bookDetailList c.HTML(200, "mainIndex.tmpl", data) } // 判断所给路径文件/文件夹是否存在 func exists(path string) bool { _, err := os.Stat(path) //os.Stat获取文件信息 if err != nil { if os.IsExist(err) { return true } return false } return true } func BoatClientInit(c *gin.Context) { bookId := c.PostForm("bookId") readTypeStr := c.PostForm("readType") bookName := c.PostForm("bookName") jumpMark := c.PostForm("jumpMark") jumpPageNumStr := c.PostForm("jumpPageNum") fmt.Println("书名:", bookName, "BookId:", bookId, "readType:", readTypeStr) //fmt.Println("书签:", jumpMark) //fmt.Println("跳转到:", jumpPageNumStr) book := conf.Book{} list := bookList for _, d := range list { if string(d.BookName) == bookName { book.BookId = d.BookId book.BookName = d.BookName book.RealPath = d.RealPath break } } bookDetail := BookDetail{} bookDetail.BookId = book.BookId bookDetail.BookName = book.BookName bookDetail.RealPath = book.RealPath bookDetail.JumpMark = jumpMark jumpPageNum, err := strconv.Atoi(jumpPageNumStr) if err != nil { jumpPageNum = 1 } bookDetail.JumpPageNum = jumpPageNum bookDetail.JumpMark = jumpMark readType, err := strconv.Atoi(readTypeStr) if err != nil { readType = 0 } bookDetail.ReadType = readType c.HTML(200, "client.tmpl", bookDetail) } func BoatClient(c *gin.Context) { pageContent := initPageContent(c) //fmt.Printf("pageContent %+v\n", *pageContent) var bookContentUTF8 string = "" if _, ok := contentMap[pageContent.BookName]; ok { bookContentUTF8 = contentMap[pageContent.BookName] } else { bookContentGBK := ReadFile(pageContent.BookRealPath) dec := mahonia.NewDecoder("gbk") bookContentUTF8 = dec.ConvertString(bookContentGBK) //去除任意的空白符 //reg := regexp.MustCompile(`[\s]+`) //bookContentUTF8 = reg.ReplaceAllString(bookContentUTF8, "") contentMap[pageContent.BookName] = bookContentUTF8 } if pageContent.ReadType == 1 { //jumpMark 处开始阅读 index := findZhongWenSubStrIndex(bookContentUTF8, pageContent.JumpMark) if index == -1 { pageContent.PageNum = 1 } else { //根据index 设置PageNum if index%pageContent.MaxLetters == 0 { pageNum := index / pageContent.MaxLetters if pageNum == 0 { pageContent.PageNum = 1 } else { pageContent.PageNum = pageNum } } else { pageContent.PageNum = index/pageContent.MaxLetters + 1 } } } else if pageContent.ReadType == 2 { //jumpPageNumStr 跳转到第 jumpPageNum页开始阅读 pageContent.PageNum = pageContent.JumpPageNum } else if pageContent.ReadType == 3 { //readType 3 直接从头开始阅读 pageContent.PageNum = 1 } totalWords := utf8.RuneCountInString(bookContentUTF8) if totalWords%pageContent.MaxLetters == 0 { pageContent.TotalPage = totalWords / pageContent.MaxLetters } else { pageContent.TotalPage = totalWords/pageContent.MaxLetters + 1 } if pageContent.PageNum > pageContent.TotalPage { c.HTML(http.StatusOK, "pagefinish.tmpl", nil) return } firstIndex := (pageContent.PageNum - 1) * pageContent.MaxLetters lastIndex := pageContent.PageNum * pageContent.MaxLetters if lastIndex+1 >= totalWords { lastIndex = totalWords - 1 } runes := []rune(bookContentUTF8) content := string(runes[firstIndex:lastIndex]) pageContent.PContent = content //fmt.Println("本页长度:", utf8.RuneCountInString(pageContent.PContent), "完整切割:本页内容") //fmt.Println(pageContent.PContent) c.HTML(http.StatusOK, "page.tmpl", pageContent) } func BoatBookMark(c *gin.Context) { jumpMark := c.PostForm("jumpMark") bookId := c.PostForm("bookId") book := conf.Book{} list := bookList for _, d := range list { if string(d.BookId) == bookId { book.BookId = d.BookId book.BookName = d.BookName book.RealPath = d.RealPath break } } //加载书签文件 jumpMarkPath := strings.ReplaceAll(book.RealPath, ".txt", "_jumpMark.txt") WriteFileGBK(jumpMarkPath, jumpMark) c.JSON(200, gin.H{"message": "保留书签成功!"}) } func WriteFileGBK(filePath string, content string) { /* O_RDONLY int = syscall.O_RDONLY // 只读打开文件和os.Open()同义 O_WRONLY int = syscall.O_WRONLY // 只写打开文件 O_RDWR int = syscall.O_RDWR // 读写方式打开文件 O_APPEND int = syscall.O_APPEND // 当写的时候使用追加模式到文件末尾 O_CREATE int = syscall.O_CREAT // 如果文件不存在,此案创建 O_EXCL int = syscall.O_EXCL // 和O_CREATE一起使用, 只有当文件不存在时才创建 O_SYNC int = syscall.O_SYNC // 以同步I/O方式打开文件,直接写入硬盘. O_TRUNC int = syscall.O_TRUNC // 如果可以的话,当打开文件时先清空文件 */ _, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644) if err != nil { fmt.Println(err.Error()) return } enc := mahonia.NewEncoder("gbk") convertString := enc.ConvertString(content) bytes := []byte(convertString) ioutil.WriteFile(filePath, bytes, 0644) } func findZhongWenSubStrIndex(str string, substr string) int { if strings.Contains(str, substr) { runes := []rune(str) firstIndex := 0 totalWords := utf8.RuneCountInString(str) for { lastIndex := firstIndex + utf8.RuneCountInString(substr) content := string(runes[firstIndex:lastIndex]) if content == substr { break } if lastIndex > totalWords-1 { break } firstIndex++ } return firstIndex } else { return -1 } } func ReadFile(filePath string) string { strBuilder := strings.Builder{} f, err := os.Open(filePath) if err != nil { fmt.Println("can't opened this file") } defer f.Close() b := make([]byte, 1024) var counter int = 0 for { read, err := f.Read(b) if err != nil && err != io.EOF { log.Println("读文章出错!", err) strBuilder.WriteString("") break } if err == io.EOF { strBuilder.WriteString("") break } s := string(b[0:read]) strBuilder.WriteString(s) counter++ } //解决文件读取比较慢,获取内容跟不上下面的分页程序的问题 var str string var spinningCounter int = 0 for { time.Sleep(200 * time.Millisecond) if counter == 0 { str = "" break } str = strBuilder.String() if len(str) > 1 { break } if spinningCounter > 200 { break } spinningCounter++ } return str } func initPageContent(c *gin.Context) *PageContent { cWidth := c.PostForm("cWidth") clientWidth, _ := strconv.Atoi(cWidth) cHeight := c.PostForm("cHeight") clientHeight, _ := strconv.Atoi(cHeight) fontSize := calcFontSize(clientWidth) pageHeight := clientHeight - fontSize*5 lineSize := calcLineSize(pageHeight, fontSize) originalX := clientWidth - fontSize*4 originalY := fontSize * 3 bookId := c.PostForm("bookId") pageNumStr := c.PostForm("pageNum") pageNum, err := strconv.Atoi(pageNumStr) if err != nil { pageNum = 1 } maxLettersStr := c.PostForm("maxLetters") maxLetters, err := strconv.Atoi(maxLettersStr) if err != nil { maxLetters = ((originalX - fontSize*2) / (fontSize * 2)) * lineSize maxLetters = (lineSize - maxLetters%lineSize) + maxLetters } book := findByBookId(bookId) pageContent := &PageContent{ BookId: bookId, BookName: book.BookName, BookRealPath: book.RealPath, PWordsCount: 0, PContent: "", ClientWidth: clientWidth, ClientHeight: clientHeight, FontSize: fontSize, OriginalX: originalX, OriginalY: originalY, PageHeight: pageHeight, LineSize: lineSize, MaxLetters: maxLetters, PageNum: pageNum, TotalPage: 0, } //处理阅读书签 readTypeStr := c.PostForm("readType") jumpMark := c.PostForm("jumpMark") jumpPageNumStr := c.PostForm("jumpPageNum") readType, err := strconv.Atoi(readTypeStr) jumpPageNum, err := strconv.Atoi(jumpPageNumStr) pageContent.JumpMark = jumpMark if err != nil { readType = 0 } pageContent.ReadType = readType if err != nil { jumpPageNum = 1 } pageContent.JumpPageNum = jumpPageNum return pageContent } func findByBookId(bookId string) conf.Book { book := conf.Book{} list := bookList for _, d := range list { if string(d.BookId) == bookId { book.BookId = bookId book.BookName = d.BookName book.RealPath = d.RealPath break } } return book } /*** GBK 1个字占 2个字节 2byte 1MB = 1024 kb 1kb = 1024 b 所以 1MB 的书可以装多少字 1kb = 512 个字 1MB 可以装 1024 * 512 = 524,288 52 万字 100MB 可以装 52 * 100 = 5200 万字 */
<!DOCTYPE > <html> <head> <title>传统文化书单</title> </head> <style> html, body { width: 100%; height: 100%; margin: 0px; background: url(/static/book/img/page.png) repeat-x; } .main{ padding-top: 100px; width: 100%; height: 100%; overflow: hidden; height: 1800px; } .row{ padding-top: 20px; padding-left: 10%; width: 100%; text-align: center; } .book{ background: url(/static/book/img/book.png) no-repeat center; margin: 10px; display: inline-block; width: 300px ; height: 180px; padding-top: 10px; font-size: 40px; float: left; cursor:pointer; cursor: hand; } .clearFloat{ clear: both; } .zi { margin-top: 30px; margin-left: 108px; line-height: 40px; writing-mode: vertical-rl; -webkit-writing-mode: vertical-rl; -ms-writing-mode: vertical-rl; font-size: 14px; font-family: '楷体'; float: left; } .menu{ font-size:12px ; background: #DDDDDD ; opacity: 0.9; width: 200px; text-align: left; visibility: hidden; } .menu ul{ padding:2px; line-height: 30px; list-style: none; cursor: hand; cursor: pointer; } ul li:hover { background: #32CD32; } .jump{ width: 20px; } </style> <body> <div class="main"> <div class="row"> {{range .bookDetailList}} <div class="book"> <div class="zi" myBookId="{{.BookId}}" myJumpMark="{{.JumpMark}}" >{{.BookName}}</div> </div> {{end}} </div> <div class="clearFloat"></div> <div id="menu" class="menu" > <ul> <li id="startReadLi" ><a>开始阅读</a></li> <li id="jumpMarkLi" >跳转到标签:<a id="jumpMarkText">孝者天之经地之义也...</a></li> <li>跳转到第<input type="text" id="jumpInput" onclick="null" onbeforepaste="jumpPaste()" onchange="jumpChange(this)" class="jump" value="1">页<a id="jumpPageNumLi" >【GO】</a></li> </ul> </div> <div class="form"> <form id="form1" method="post" action="/boat/clientInit"> <input type="hidden" id="bookName" name="bookName" value="" /> <input type="hidden" id="bookId" name="bookId" value="" /> <input type="hidden" id="jumpMark" name="jumpMark" value="" /> <input type="hidden" id="jumpPageNum" name="jumpPageNum" value="1" /> <input type="hidden" id="readType" name="readType" value="3" /> </form> </div> </div> </body> <script> var mousePosition = {}; function mouseMove(ev) { Ev = ev || window.event; var mousePos = mouseCoords(ev); mousePosition['x'] = mousePos.x; mousePosition['y'] = mousePos.y; } function mouseCoords(ev) { if (ev.pageX || ev.pageY) { return { x: ev.pageX, y: ev.pageY }; } return { x: ev.clientX + document.body.scrollLeft - document.body.clientLeft, y: ev.clientY + document.body.scrollTop - document.body.clientTop }; } document.onmousemove = mouseMove; </script> <script> var timeout ; var menu={ bookId:"", bookName:"", jumpMark:"", jumpPageNum:0, readType:0 }; function showMenu(objBook) { var menuDiv =document.getElementById("menu"); menuDiv.style.visibility = "visible"; menuDiv.style.left = mousePosition.x + 'px'; menuDiv.style.top = mousePosition.y + 'px'; menuDiv.style.position='absolute'; var jumpMarkText = document.getElementById("jumpMarkText"); var mark = objBook.jumpMark; mark = mark.replace(/\s*/g,"") jumpMarkText.innerText = mark; clearTimeout(timeout); hiddenTimeOut(menuDiv,20000); menu.bookId = objBook.bookId; menu.bookName = objBook.bookName; menu.jumpMark = objBook.jumpMark; } function hiddenTimeOut(obj,milliseconds){ timeout = window.setTimeout(function(){ if(obj.style.visibility = "visible"){ obj.style.visibility = "hidden"; } },milliseconds); } function submitRead(thisMenu){ document.getElementById("bookId").value = thisMenu.bookId; document.getElementById("bookName").value = thisMenu.bookName; document.getElementById("jumpMark").value = thisMenu.jumpMark; try{ var thisJumpPageNum = parseInt(thisMenu.jumpPageNum); document.getElementById("jumpPageNum").value = thisJumpPageNum; if((thisJumpPageNum<=0) && (thisMenu.readType==2)){ alert("请输入正确的页码!"); return } }catch(e){ alert("请输入正确的页码!"); } document.getElementById("readType").value = thisMenu.readType; document.getElementById("form1").submit(); } function jumpPaste(){ window.clipboardData.setData('text',clipboardData.getData('text').replace(/^[0-9]*$/g,'')); } function jumpChange(obj){ if(!/^[0-9]*$/.test(obj.value)){ obj.value=''; alert('请输入正确的页码!'); } } window.onload = function() { document.getElementById("startReadLi").onclick = function(event){ menu.readType = 3; submitRead(menu); event.stopPropagation(); } document.getElementById("jumpMarkLi").onclick = function(event){ menu.readType = 1; submitRead(menu); event.stopPropagation(); } document.getElementById("jumpPageNumLi").onclick = function(event){ menu.readType = 2; menu.jumpPageNum = document.getElementById("jumpInput").value; submitRead(menu); event.stopPropagation(); } var books = document.getElementsByClassName("book"); for (var i = 0; i < books.length; i++) { var book = books[i]; book.onclick = function() { var myBookId = this.children[0].getAttribute("myBookId"); var myBookName = this.children[0].innerText; var myJumpMark = this.children[0].getAttribute("myJumpMark"); var bookQuery = { bookId : myBookId, bookName : myBookName, jumpMark : myJumpMark }; showMenu(bookQuery); } } } </script> </html>
<!DOCTYPE html> <html> <head lang="en"> <style> html, body { width: 100%; height: 100%; margin: 0px; /* overflow: hidden; */ /* background-image: linear-gradient(#BEBEBE,#778899) ; */ background: url(/static/book/img/page.png) repeat-x; cursor:pointer; cursor: hand; } .main{ text-align: center; padding: 10px; } .page{ } .pageFoot{ width: 100%; padding-top: 10px; padding-bottom: 10px ; /* height: 40px; */ /* border: 1px solid red;*/ text-align: center; } .prev{ border: 1px solid #008080; background:#E6E6FA; padding-top: 5px; padding-bottom: 5px; padding-left: 60px; padding-right: 60px; border-radius: 2px; text-decoration: none; /* font-size: 20px; */ } .next{ border: 1px solid #008080; background: #F0F8FF; padding-top: 5px; padding-bottom: 5px; padding-left: 60px; padding-right: 60px; border-radius: 2px; text-decoration: none; /* font-size: 20px; */ } .current { border: 1px solid #008080; background: #54FF9F; padding-top: 5px; padding-bottom: 5px; padding-left: 60px; padding-right: 60px; border-radius: 2px; text-decoration: none; } .helpWin{ border: 1px solid #008080; background: #7FFF00; padding-top: 5px; padding-bottom: 5px; padding-left: 60px; padding-right: 60px; border-radius: 2px; text-decoration: none; } .mark{ border: 1px solid #008080; background: #7FFF00; padding-top: 5px; padding-bottom: 5px; padding-left: 60px; padding-right: 60px; border-radius: 2px; text-decoration: none; } .backMain{ border: 1px solid #008080; background: #7FFF00; padding-top: 5px; padding-bottom: 5px; padding-left: 60px; padding-right: 60px; border-radius: 2px; text-decoration: none; } a:hover{ background: limegreen; text-decoration: none; } .jumpBox{ border: 1px solid #008080; background: #7FFF00; padding-left: 10px; padding-right: 10px; border-radius: 2px; padding-top: 5px; padding-bottom: 5px; } .jump{ display: inline-block; border: 1px solid #008080; border-bottom: 1px solid #000000 ; height: 25px; margin-bottom: 20x; border-radius: 2px; width: 40px; text-align: center; } .searchTxt{ margin: 0; padding: 0; height: 30px; } .searchTxt input{ display: inline-block; height: 25px; width: 92%; float: left; } .searchTxt .booklogo{ display: inline-block; height: 30px; border-radius: 2px ; float: left; } .searchTxt .searchlogo{ display: inline-block; height: 30px; background: #7FFF00; border: 1px solid #DDDDDD; border-radius: 2px ; float: left; } </style> <meta charset="UTF-8"> <title></title> </head> <body> <div class="main" > <canvas id='canvas' class="page" ></canvas> </div> <div class="searchTxt"> <img class="booklogo" src="/static/book/img/booklittle.png" /> <input type="text" id="searchTxt" name="searchTxt" value="" /> <img class="searchlogo" onclick="doSearch()" src="/static/book/img/search.png" /> </div> <div class="pageFoot"> <a class="prev" onclick="prev()" ><</a> <a class="current" onclick="current()" >刷新</a> <a class="next" onclick="next()" >></a> <a class="helpWin" onclick="doHelp()" href="javascript:void(0);">帮助</a> <a class="mark" onclick="mark()" href="javascript:void(0);">保存书签</a> <a class="backMain" onclick="backMain()" href="javascript:void(0);">返回主页</a> <a class="jumpBox" onclick="jump()" >共{{.TotalPage}}页,当前第{{.PageNum}}页,转到<input type="text" id="jumpPageNum" onclick="null" onbeforepaste="jumpPaste()" onchange="jumpChange(this)" class="jump" />页</a> </div> <div> <form id="form1" method="post" action="/boat/client" > <input type="hidden" id="bookId" name="bookId" value="{{.BookId}}" /> <input type="hidden" id="pageNum" name="pageNum" value="{{.PageNum}}" /> <input type="hidden" id="maxLetters" name="maxLetters" value="{{.MaxLetters}}" /> <input type="hidden" id="cWidth" name="cWidth" value="{{.ClientWidth}}" /> <input type="hidden" id="cHeight" name="cHeight" value="{{.ClientHeight}}" /> </form> </div> <script> var mousePosition = {}; function mouseMove(ev) { Ev = ev || window.event; var mousePos = mouseCoords(ev); mousePosition['x'] = mousePos.x; mousePosition['y'] = mousePos.y; inputSearchText(mousePos.x, mousePos.y); } function mouseCoords(ev) { if (ev.pageX || ev.pageY) { return { x: ev.pageX, y: ev.pageY }; } return { x: ev.clientX + document.body.scrollLeft - document.body.clientLeft, y: ev.clientY + document.body.scrollTop - document.body.clientTop }; } document.onmousemove = mouseMove; </script> <script> var bookId = {{.BookId}} var fontSize = {{.FontSize}}; var orginalX = {{.OriginalX}}; var orginalY = {{.OriginalY}}; var pageHeight = {{.PageHeight}}; var lineSize = {{.LineSize}}; var position = { x: orginalX, y: orginalY }; var article = ' '+'{{.PContent}}'; var canvas; var context; var maxLetters = {{.MaxLetters}} ; function init() { canvas = document.getElementById('canvas'); context = canvas.getContext('2d'); canvas.width = {{.ClientWidth}}; canvas.height = {{.ClientHeight}}; drawWenZhang(); window.onresize = function(ev) { var oEvent = event || ev; canvas.width = {{.ClientWidth}}; canvas.height = {{.ClientHeight}}; } } var contentAry = []; var positionAry = []; var index = 0; function inputSearchText(mouseX,mouseY) { if(inMaxMinArea(mouseX,mouseY)){ for (var i = 0; i < positionAry.length; i++) { var p = positionAry[i]; var AryX = p.split("##"); if (mouseX > parseFloat(AryX[0]) && mouseX < parseFloat(AryX[1])) { index = i+1; break; } } if(index<contentAry.length+1){ document.getElementById("searchTxt").value = contentAry[index]; } } } function inMaxMinArea(posX,posY){ var maxY = pageHeight; if(posY>=parseFloat(maxY)){ return false; } if(positionAry.length>1){ minX = positionAry[positionAry.length-1].split("##")[0]; maxX = positionAry[0].split("##")[1] if(posX>=parseFloat(minX) && posX<=parseFloat(maxX)){ return true; } } return false; } function doSearch(){ var searchTxt = document.getElementById("searchTxt").value; window.open("https://www.baidu.com/s?wd="+ searchTxt); } function drawWhiteSpace(){ context.font = fontSize + "px Georgia"; context.save(); context.translate(position.x, position.y); context.fillText(" ", 0, 0); context.restore(); position.y = position.y + fontSize * 1.5; } var strTemp = ""; var lastStrTemp = ""; function drawWenZhang() { //drawWhiteSpace(); var cnt = 0; for (var i = 0; i < article.length; i++) { context.font = fontSize + "px Georgia"; context.save(); if(i==0){ context.translate(position.x,position.y+fontSize * 1.5); }else{ context.translate(position.x,position.y); } var ch = undefined ; try{ ch = article[i]; if(cnt>(article.length - lineSize)){ lastStrTemp = lastStrTemp + article[i]; } }catch(e){ context.restore(); break; } if(ch == undefined){ context.fillText(' ', 0, 0); context.restore(); break; }else{ strTemp = strTemp + ch; context.fillText(ch, 0, 0); context.restore(); } if (cnt == article.length-1){ break; } if (cnt % lineSize == 0) { contentAry.push(strTemp); strTemp = ""; context.save(); var flag = 0; if(cnt>0){ flag=1; } position.x = position.x - fontSize * 2*flag; position.y = orginalY ; context.moveTo(position.x - ((fontSize*2 - fontSize)/2.0) , orginalY); context.lineTo(position.x - ((fontSize*2 - fontSize)/2.0) , orginalY+pageHeight); var x1 = position.x + fontSize ; var x2 = x1 + fontSize*2 ; positionAry.push(x1 + "##" + x2); context.stroke(); context.restore(); } cnt++; position.y = position.y + fontSize * 1.5; } contentAry.push(lastStrTemp); } function jump(){ var jumpPageNumStr = document.getElementById("jumpPageNum").value; if(jumpPageNumStr!=""){ try{ var jumpPageNum = parseInt(jumpPageNumStr); document.getElementById("pageNum").value=jumpPageNum; if(jumpPageNum>0){ document.getElementById("form1").submit(); }else{ alert("请输入正确的页码!"); } }catch(e){ alert("请输入正确的页码!"); return } } } function jumpPaste(){ window.clipboardData.setData('text',clipboardData.getData('text').replace(/^[0-9]*$/g,'')); } function jumpChange(obj){ if(!/^[0-9]*$/.test(obj.value)){ obj.value=''; alert('输入数字!'); } } function urlencode (str) { str = (str + '').toString(); return encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28'). replace(/\)/g, '%29').replace(/\*/g, '%2A').replace(/%20/g, '+'); } function doHelp() { var searchTxt = document.getElementById("searchTxt").value; if(searchTxt.length>1){ alert("条目太长,限一个生字"); }else{ window.open("https://www.zdic.net/hans/"+urlencode(searchTxt)); } } function prev(){ var pageNum = document.getElementById("pageNum").value; if( pageNum == "" || pageNum == 1 ){ document.getElementById("pageNum").value = 1; }else{ document.getElementById("pageNum").value= parseInt(pageNum) - 1; } document.getElementById("form1").submit(); } function current(){ var pageNum = document.getElementById("pageNum").value; if( pageNum == "" || pageNum == 1 ){ document.getElementById("pageNum").value = 1; } document.getElementById("form1").submit(); } function next(){ var pageNum = document.getElementById("pageNum").value; if( pageNum == "" ){ document.getElementById("pageNum").value = 1; }else{ document.getElementById("pageNum").value= parseInt(pageNum) + 1; } document.getElementById("form1").submit(); } function mark(){ var content = article; var firstIndex = 1; var re = /[^'"“”?‘’《》○\s]+/; var mark = ""; for(var i=0;i<content.length;i++){ var myArray = null ; var lastIndex = firstIndex + ((content.length)/5) -1; if (lastIndex<=1){ mark = ""; break; } if(lastIndex>content.length-1){ break; } var str1 = content.substring(firstIndex,lastIndex); myArray = str1.match(re); if((myArray==null) || (myArray=="") || (myArray.length==0)){ continue; } for(var j=0;j<myArray.length;j++){ var markTemp = myArray[j]; if(markTemp.length>mark.length){ mark = markTemp; } } firstIndex++; } if(mark.length>1){ if( mark.substring(0,1)==","){ mark = mark.substring(1); } } alert(mark); send_request(mark,bookId); } function backMain(){ window.location.href = "/boat/index"; } function send_request( mark,bookId ){ var xmlhttp = null; if (window.XMLHttpRequest){ xmlhttp=new XMLHttpRequest(); }else if (window.ActiveXObject){ xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); } if (xmlhttp!=null){ var requrl = '/boat/mark'; xmlhttp.open( "POST",requrl,true); xmlhttp.onreadystatechange=function(){ //异步需要指定回调函数 if (xmlhttp.readyState==4 && xmlhttp.status==200){ //readyState为4,表示ajax请求已经完成,status是目标url返回的http状态码,200表示服务器响应成功 var d= xmlhttp.responseText; // 处理返回结果 alert(d); } } //post需要设置Content-type,防止乱码 xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded"); //post需要将参数放在send方法,当然参数放在url也还是可以的,但不好 var jumpMarkStr = mark; xmlhttp.send("jumpMark="+jumpMarkStr+"&bookId="+ bookId); }else{ alert("您的浏览器不支持AJAX!"); } } window.onload = function(){ init(); } </script> </body> </html>
2.效果如下:
源代码公开,欢迎修改转载使用。
https://pan.baidu.com/s/1NW_Am0WsjwrTtcfZKVTkOg 提取码: ptaw
下载地址:https://files.cnblogs.com/files/javatiandi/DuRenZhou.rar