【感謝@AvisBlume 的熱心翻譯。如果其他朋友也有不錯的原創或譯文,可以嘗試提交到伯樂線上。】
還記得在學校裡學大O符號(Big O notation) 反覆實現各種排序演算法的那些日子嗎?如果你是像我這樣的一個網頁開發工程師,那麼你很可能永遠也不會用到這些演算法了。
不要表示反對,我的意思是說,網頁開發中99%的工作都是在處理瀏覽器的各種異常,還有就是在糾結其他人的程式碼。
然後,演算法知識突然就變得非常有用了。
產生想法
從Chromatic專案一開始,我們就想讓照片儘可能地大。不要縮圖,不要裁剪,也不要浪費螢幕上寶貴的每寸地方。
相簿通常會含有10到100張長寬比各異的照片,我們的目的是要讓它們一行一行地平均分佈在所有可用空間內。
最後我們決定:讓照片看起來都一樣高,每一行進行微調從而讓照片充滿整行空間。
遇到問題
但我們該如何佈置這些照片呢?如果不發帖問下“我要怎樣才能得到某個庫來實現某個功能”的話,在StackOverflow網上就很難找到答案了。
於是我們開始從演算法的角度來思考我們遇到的問題:這會不會是一個已知的標準問題?可不可以將它變成一個標準問題?
如果是這樣的話,那很可能已經有人設計出一種演算法來解決這個問題了。
實際上也確實如此,這屬於劃分問題。更好的是已經有個Python版的實現了,我們後來把它移植到了Coffeescript中。
解決辦法
剩下的就很簡單了。
為了找出所需的行數(k),我們先將照片高度調整到視窗的一半(因為我們發現調整到一半的時候照片看起來和諧),將它們的寬度加起來,然後除以視窗的寬度。結果可能不是整數,因此要將其取整。
照片的高寬比(乘以100以後取整)看做高度的集合(S)。然後我們就用新得的演算法工具根據S和k來找出最佳解法。
計算結果告訴我們每行照片的數量。然後要做的就是選用適當大小的照片,進行調整後放入可用的水平空間中。
下面是 Backbone 照片庫(gallery)檢視的相關程式碼。實際線性分割槽演算法可以在這裡找到。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
viewport_width = $(window).width() ideal_height = parseInt($(window).height() / 2) summed_width = photos.reduce ((sum, p) -> sum += p.get('aspect_ratio') * ideal_height), 0 rows = Math.round(summed_width / viewport_width) if rows < 1 # (2a) Fallback to just standard size photos.each (photo) -> photo.view.resize parseInt(ideal_height * photo.get('aspect_ratio')), ideal_height else # (2b) Distribute photos over rows using the aspect ratio as weight weights = photos.map (p) -> parseInt(p.get('aspect_ratio') * 100) partition = linear_partition(weights, rows) # (3) Iterate through partition index = 0 row_buffer = new Backbone.Collection _.each partition, (row) -> row_buffer.reset() _.each row, -> row_buffer.add(photos.at(index++)) summed_ratios = row_buffer.reduce ((sum, p) -> sum += p.get('aspect_ratio')), 0 row_buffer.each (photo) -> photo.view.resize parseInt(viewport_width / / summed_ratios * photo.get('aspect_ratio')), parseInt(viewport_width / summed_ratios) |
做這個花了不少工夫,但是,哈哈,來看看效果吧!