這篇文章要跟大家介紹的內容是關於canvas實現九宮格心型拼圖的方法(附程式碼),有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。
前幾天在朋友圈看到好幾次這種圖片。
這種圖片,是用九張圖片拼成的心形。
感覺很有趣,就上網查了查怎麼做,大部分的說法就是用美圖秀秀的拼圖功能來做, 在微信小程序中也有專門做心形拼圖的小程序,我都試了試之後,感覺還可以更簡單一些,於是我就自己做了個小程式。
#1、有兩個canvas,一個小的canvas 顯示最後會是什麼樣子,一個大的canvas 用來最後進行截圖,產生圖片,保存到相簿。
透過CSS的定位,把大的 canvas 移到螢幕外不讓使用者看到就可以了。
而如果用小小的canvas 儲存圖片的話,最後的圖片有些模糊。
2、canvas 可以看成一個9 * 9 的網格,
用一個叫heart 的陣列來表示就是這樣的。
用其中的小格子,來拼出心形,根據陣列的內容在 canvas 上進行渲染。
這個小程式有 選擇單張圖片,選擇多張圖片,補充圖片,儲存圖片,重置,推薦,意見回饋,這幾個功能。
當使用者點擊心形區域的時候,就可以選擇單張圖片,呼叫wx.chooseImage 就可以從本地相簿選擇圖片,然後就把這張圖,畫在canvas上,具體畫的位置就是使用者點擊的位置。
在小的canvas上綁定touchend 事件,觸發事件後,事件中有一個changedTouches 屬性,這是一個保存了,當前變化的觸摸點信息的數組,這個數組中的元素有x 和y 屬性,也就是觸控點距離canvas 左上角的距離。
// 触摸点在 x 轴的值 var x = e.changedTouches[0].x; // 触摸点在 y 轴的值 var y = e.changedTouches[0].y;
有 x 軸 和 y 軸的距離後,算出具體應該畫在哪個格子上。
//grid 表示一个格子的宽度 // 确定 x 轴是在第几个格子 x = Math.floor(x / grid); // 确定 y 轴是在第几个格子 y = Math.floor(y / grid);
知道在哪個格子畫之後,就要確定畫圖片的哪一部分了,因為所有的格子都是正方形的,但是用戶選擇的圖片不一定是正方形,如果壓縮成正方形會很難看,所以我畫的時候,選擇了正中間的部分來畫,
通過wx.getImageInfo 來獲取圖片信息,以短邊為正方形的寬,然後從(長邊- 短邊)/2
的地方來畫。
// 获取图片的宽和高 var width = res.width; var height = res.height; // 如果图片不是正方形,只画中间的部分 // sWidth 表示正方形的宽 var sWidth = width > height ? height : width; // sx 是源图像的矩形选择框的左上角 X 坐标 var sx = 0; // sy 是源图像的矩形选择框的左上角 y 坐标 var sy = 0; if (width > height) { sx = (width - height) / 2; } if (width知道要畫什麼,在哪裡畫之後,呼叫 canvasContext.drawImage 來畫就可以了。
選擇多張圖片
選擇多張圖片,同樣是呼叫wx.chooseImage 方法,成功選擇多張圖片後,返回的物件中有一個tempFilePaths 屬性,這個屬性保存了,圖片的本機檔案路徑清單。
然後遍歷heart 數組,也就是保存心形資料的數組,如果數組中某個元素的值是1,也就是說在心形範圍內,就依序從tempFilePaths 取一張圖片畫上去,畫的時候同樣的,如果不是正方形就只畫中間的部分。
補充圖片
在 image 的檔案中,有保存幾張圖片,用來補充心形,他們的路徑保存在一個陣列中。
// 用来补充心形的图片 images: [ '../../images/1.jpg', '../../images/2.jpg', '../../images/3.jpg', '../../images/4.jpg', '../../images/5.jpg', '../../images/6.jpg', '../../images/7.jpg', '../../images/8.jpg', '../../images/9.jpg', '../../images/10.jpg', ]登入後複製
然後就是遍歷 heart 數組,如果數組的某個元素的值是1,就隨機從這組圖片中選擇一張畫上去。
畫一張圖片,畫多張圖片,補充圖片,他們都是在 canvas 上畫圖片,為了避免已經畫了圖片的位置被覆蓋,他們所畫的圖片的等級是不同的。
补充图片:1 画多张图片:2 画一张图片:3
等級高的可以覆蓋等級低的,等級低的不能覆蓋等級高的,同等級的,除了畫多張圖片的不能覆蓋,其餘的兩種情況,都可以覆蓋。
简单意思就是:
补充图片,补充完了之后,再补充会把原来补充的覆盖掉,但是用户选择的图片不会被覆盖掉。
画多张图片,可以覆盖掉补充的图片,但用户选择的图片也不会覆盖掉。
画一张图片,不管这个位置有没有图片,都会再画一张。
保存图片的时候,就是按顺序对大的 canvas 进行截取,然后保存成图片,主要靠 wx.canvasToTempFilePath 这个API来实现,这个 API ,可以把当前画布指定区域的内容导出生成指定大小的图片,并返回文件路径。
这里要注意几个细节
1、为了避免最后保存的图片有黑色背景,最好开始的时候就在 canvas 上画一个 和 canvas 大小一样的矩形,矩形填充上颜色。
2、为了保存的图片,在用户的相册中也能保持心形。需要按下面这个顺序来保存图片
3、wx.canvasToTempFilePath 中有两个选填的参数 destWidth 和 destHeight,这个两个参数决定 输出图片宽度和高度,如果不是准确的知道是多少,用默认值就可以。
destWidth
和destHeight
单位是物理像素(pixel),canvas 绘制的时候用的是逻辑像素(物理像素=逻辑像素 * density),所以这里如果只是使用 canvas 中的 width 和 height(逻辑像素)作为输出图片的长宽的话,生成的图片 width 和 height 实际上是缩放了到 canvas 的1 / density
大小了,所以就显得比较模糊了。
而默认值是 width * 屏幕像素密度
文档中提到的屏幕像素密度,应该不是指每英寸屏幕所拥有的像素数,而是指设备像素比(pixelRatio),也就是用多少个物理像素去显示 1px 的 CSS 像素。
用API wx.getSystemInfo 可以查看设备像素比
wx.getSystemInfo({ success: function(res) { console.log(res.pixelRatio) } })
这里如果我的理解有误,还请知道的小伙伴指出。
说了这么多,主要就是想说用默认的值其实就已经很清晰了。
4、因为要保存9张图片,所以需要一些时间,这个时候就需要一个进度条了,保存图片的时候,显示进度条,禁用保存按钮,毕竟点击一下按钮就是9张图片,所以这个时候还是禁用了好,每保存一张图片进度条的值就 +12 ,超过100的时候,就表示 9张图片都保存好了。
而微信小程序中也刚好有进度条(progress)这个组件。
这个功能就是遍历 heart 数组,用一种颜色,根据数组内容,把心形画出来。然后再在 x 轴 和 y 轴上画两条线,行成九宫格的样子。
这个两个功能就是用了,微信小程序的 button 组件,这里需要注意的就是,在清除 button 的默认样式时,需要把button 的 after
伪元素的边框也去掉。
button::after{ border: 0; }
有一些地方是小程序在替用户做选择,比如,如果所选择的图片不是正方形,就画中间的部分,但是中间的部分不一定是用户想要的,而如果每张图片都要用户自己来选择画哪部分,一共81张图片,显然是有些麻烦了,这里还可以继续优化下。
还有在补充图片的时候,补充的图片也不一定是用户喜欢的,所以这部分再考虑是不是可以加一些标签,用户选择不同的标签,来补充符合标签的图片,类似 QQ音乐的歌词海报这样。
这次做的这个九宫格心形拼图的小程序,第一版已经上线了。
开源地址:https://github.com/FEWY/jigsaw
相关文章推荐:
HTML5 Canvas实现交互式地铁线路图
以上是canvas實作九宮格心形拼圖的方法(附程式碼)的詳細內容。更多資訊請關注PHP中文網其他相關文章!