Discuz功能改進-從代碼修改到插件開發:頭像編輯


這個文章是基於頭像上傳HTML(原文)的插件開發,天香老師將Discuz 頭像上傳更改成html5後續其實繼續修改,將這個功能給插件化,我相信這給了許多Discuz愛好非常大的便利性,廢話也就不多說了,接下來一樣由天香老師講解。

※本人只翻譯成繁體中文,並修正部分文句讓臺灣這邊Discuz站長可以看更清楚,可能在修正文句上並不完美,請見諒。

※本文經過天香老師同意轉載並修正文句,若要再轉載修正文句,請先經過原作者同意,尊重原作者,原作者保留修改及調整權利。


不久前應網友aikato的要求把頭像編輯功能由flash改成了html5 ( 鏈接 )。 當時沒對這事有多大興趣因為這不是個網民每次上網都要用到的功能,也許一個網民在一個網站就用一次。 沒想到貼出來後好幾位網友表示不喜歡flash而偏好html5。 網友carry0987向我介紹了他人寫的頭像插件和其它網站上對這個功能的修改,所以我也來嘗試下將我的修改代碼改成插件形式。

編碼和解碼

回想起來當時沒有寫成插件一是因為沒有需要,二是由於當頭像在flash編輯後上傳到服務器後,服務器端的程序對上傳上來的圖片數據做了個解碼,這說明flash程序裡對圖像數據做了某種編碼。 而html5則用了另一種稱為base64的編碼,所以在服務器端需要對數據做base64的解碼。 當時覺得如果不直接修改Discuz代碼來支持這種解碼而寫插件的話,就要拷貝很多Discuz代碼到插件裡就有些無聊了。 這次重新考慮了下,發現我們可以在客戶端將html5生成的用base64編碼的圖片數據先解碼,再按服務器端的flash解碼方式在客戶端先做對應的編碼,這樣上傳的圖片數據就和原來完全相同,因而就不用修改服務器端的代碼了。

Discuz的解碼程序在文件uc_server/control/user.php 裡:
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">function flashdata_decode($s) { $r = &#39;&#39;; $l = strlen($s); for($i=0; $i&lt;$l; $i=$i+2) { $k1 = ord($s[$i]) - 48; $k1 -= $k1 &gt; 9 ? 7 : 0; $k2 = ord($s[$i+1]) - 48; $k2 -= $k2 &gt; 9 ? 7 : 0; $r .= chr($k1 &lt;&lt; 4 | $k2); } return $r; }</span> function flashdata_decode($s) { $r = &#39;&#39;; $l = strlen($s); for($i=0; $i&lt;$l; $i=$i+2) { $k1 = ord($ s[$i]) - 48; $k1 -= $k1 &gt; 9 ? 7 : 0; $k2 = ord($s[$i+1]) - 48; $k2 -= $k2 &gt; 9 ? 7 : 0; $r .= chr($k1 &lt;&lt; 4 | $k2); } return $r; }</span>
這個編碼基於常用的16進制數表示:0到9的數字不變,而10到15分別用字母A到F來表示。 用這種0到15這16個數字和0到9加上A到F共16個字符間的一一對換,每個8位數據都可以用兩個0到9加上A到F的字符組合表示,而解碼就是兩個0到9加上A到F的字符組合轉換成一個8位數據。
舉例而言:200 = 12 * 16 + 8 => (12, 8) => (C, 8) => C8。 理解了這一點,我們就不難在網頁代碼裡重現原來flash裡的編碼程序(因為是javascript代碼,用到的函數和原來的php代碼有所不同,本質是一樣的):
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">function flashdata_encode(s) { var r = &#39;&#39;; var l = s.length; for (var i = 0; i &lt; l; i++) { var k = s[i].charCodeAt(0); var k1 = k &gt;&gt; 4; var k2 = k - (k1 &lt;&lt; 4); k1 += 48; if (k1 &gt; 57) k1 += 7; k2 += 48; if (k2 &gt; 57) k2 += 7; r += String.fromCharCode(k1, k2); } return r; }</span> function flashdata_encode(s) { var r = &#39;&#39;; var l = s.length; for (var i = 0; i &lt; l; i++) { var k = s[i].charCodeAt(0); var k1 = k &gt;&gt; 4; var k2 = k - (k1 &lt;&lt; 4); k1 += 48; if (k1 &gt; 57) k1 += 7; k2 += 48; if (k2 &gt; 57) k2 += 7; r + = String.fromCharCode(k1, k2); } return r; }</span>

一些改進:

1)頭像圖片的三種來源:原來的代碼修改旨在替換原有的頭像編輯功能,所以只支持上傳用戶自己機器上的圖片做為頭像。 但為什麼不能像日誌裡添加圖片那裡那樣,也支持用相冊圖片和網絡圖片做為頭像呢? 所以在這個插件裡我支持了上傳圖片,相冊圖片和網絡圖片三種圖片來源:



界面的代碼參照了文件template/default/home/editor_image_menu.htm,而處理提交選擇後的圖片的代碼參照了文件uc_server/control/user.php。 幸好裁剪圖片的代碼不管圖片來自何方,所以並不需要為支持相冊圖片和網絡圖片而添加很多代碼。

2)以flash 為後備(fallback):以html5替代flash固然好,但萬一有用戶用的是舊的瀏覽器不支持html5怎麼辦呢? 為此我加了一段判斷用戶的瀏覽器是否支持html5的畫布功能。 如果支持就用我們的html5功能,不然還是顯示原來的flash功能:
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">if (isCanvasSupported()) { replaceAvatarSection(document.getElementById(&#39;avatardesigner&#39;)); ... } //http://stackoverflow.com/questions/2745432/best-way-to-detect-that-html5-canvas-is-not-supported function isCanvasSupported() { var elem = document.createElement(&#39;canvas&#39;); return !!(elem.getContext &amp;&amp; elem.getContext(&#39;2d&#39;)); }</span> if (isCanvasSupported()) { replaceAvatarSection(document.getElementById(&#39;avatardesigner&#39;)); ... } //http://stackoverflow.com/questions/2745432/best-way-to-detect-that-html5-canvas -is-not-supported function isCanvasSupported() { var elem = document.createElement(&#39;canvas&#39;); return !!(elem.getContext &amp;&amp; elem.getContext(&#39;2d&#39;)); }</span>

3)在javascript文件裡使用語言包裡的漢字:在插件中的javascript文件裡要使用漢字比如提示框裡要顯示漢字怎麼辦? 有此問題是在javascript文件裡不像在php文件和html文件能調用語言包裡定義的漢字。 Discuz代碼自身的做法是在有些javascript文件裡直接把漢字寫在裡面,因此對於這些文件,Discuz的每個語言版本就提供了不同版本的文件(而不是像其它文件那樣通用)。 而且這種做法也不適合要支持多種語言版本的插件因為把插件放到Discuz應用中心時沒法提供多個語言版本的同一javascript文件。 看到Discuz網站上一個討論( 鏈接 )給出了個很好的解決方法:在html文件裡調用語言包裡的漢字來定義javascript變量值,而在javascript文件裡調用這些變量:
在模板文件avatar.htm 裡:
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">&lt;script type=&quot;text/javascript&quot;&gt; var upload_succeed = &#39;{lang txgz_avatar:upload_succeed}&#39;; var three_avatars = &#39;{lang txgz_avatar:three_avatars}&#39;; var upload_error = &#39;{lang upload_error}&#39;; &lt;/script&gt; &lt;script type=&quot;text/javascript&quot; src=&quot;source/plugin/txgz_avatar/js/avatar.js?{VERHASH}&quot;&gt;&lt;/script&gt;</span> &lt;script type=&quot;text/javascript&quot;&gt; var upload_succeed = &#39;{lang txgz_avatar:upload_succeed}&#39;; var three_avatars = &#39;{lang txgz_avatar:three_avatars}&#39;; var upload_error = &#39;{lang upload_error}&#39;; &lt;/script&gt; &lt;script type=&quot;text/javascript&quot; src=&quot;source/plugin/txgz_avatar/js/avatar.js?{VERHASH}&quot;&gt;&lt;/script&gt;</span>
在javascript 文件avatar.js 裡:
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">ctx.fillText(upload_succeed, dwidth - 140, 180);</span> ctx.fillText(upload_succeed, dwidth - 140, 180);</span>


10/17/2016補充:寫了一個不基於HTML5且支持動畫圖片的新版本http://www.bian-wang.com/discuz/home.php?mod=space&uid=10005&do=blog&id=1533
04/26/2017更新:網友e0759提醒本插件在Discuz2.5裡不工作。 檢查發現由模版文件avatar.htm產生的腳本文件有語法錯誤。 看來是因為Discuz2.5的block標籤內不能插入eval標籤( 鏈接 ),解決辦法是將原來開始的兩句
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">&lt;!--{block avatardesigner}--&gt; &lt;!--{eval $albums = getalbums($_G[&#39;uid&#39;]); }--&gt;</span> &lt;!--{block avatardesigner}--&gt; &lt;!--{eval $albums = getalbums($_G[&#39;uid&#39;]); }--&gt;</span>
改成
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">{eval $albums = getalbums($_G[&#39;uid&#39;]); } &lt;!--{block avatardesigner}--&gt;</span> {eval $albums = getalbums($_G[&#39;uid&#39;]); } &lt;!--{block avatardesigner}--&gt;</span>
這樣就適用於2.5以上的所有版本了。
11/27/2017更新:當選擇本地圖片時,沒有必要把圖片先上傳到服務器上,可以直接顯示在圖片元素裡。 這是File API提供的功能,參見這裡的討論 。 注意網絡圖片仍有先拷貝到服務器的必要,不然在裁剪圖片時會出現跨域安全錯誤。
11/28/2017更新:防止與其它版本的jQuery發生衝突。
11/29/2017更新:將屬性裡設置的load事件處理改為在代碼裡設置,以避免在使用Cloudflare的網站裡被其更改而不工作。

插件下載: http://www.bian-wang.com/discuz/data/userupload/10005/txgz_avatar.zip (11/29/2017最後更新)










出處:彼岸網
作者:天香公主
翻譯及修改部分文句:HF
原網址:點我

留言

這個網誌中的熱門文章