整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          APP九宮格如何用Axure畫出來?

          APP九宮格如何用Axure畫出來?

          宮格用來平鋪展示很多頻道或者欄目。那么如何用Axure畫出該功能呢?

          常用場景有“微信APP-我-錢包”、“淘寶APP-首頁-頻道”、“支付寶APP-首頁”。

          一、原型畫法(無交互)

          九宮格通常包含N個格子,每行3個,每個格子由按鈕和圖標組成。

          1. 從默認元件庫拖動“中繼器”到工作區合適位置。

          2. 雙擊該中繼器進入內部,即可編輯每一個格子。

          3. 先畫按鈕。選擇矩形并修改尺寸為125*125px(每行3個,則單個寬度=375/3)。雙擊輸入文字“標題”,然后對齊方式為底部對齊。

          4. 再畫圖標。從默認元件庫拖動“圖片”到合適位置,修改尺寸為75*75px。

          5. 點擊空白區域即可設置中繼器屬性,添加更多的行,直到9個。這里輸入的內容直接代表原型中的每個格子名稱。

          6. 設置中繼器的樣式,布局改成水平,勾選網格分布,每行項目數為3。

          7. 回到工作區,就可以看到九宮格效果。

          8. 生成原型HTML并在瀏覽器中查看效果。

          二、原型畫法(有交互)

          九宮格的常見交互效果:點擊每個格子,跳轉到新頁面。

          接下來以“微信APP-我-錢包”來作為案例詳細講解。

          9. 修改每個格子的標題,雙擊Column0的每一行,依次輸入對應的標題。

          10. 然后畫出每個宮格對應的頁面,總共9個,依次創建。

          11. 雙擊該中繼器進入內部,選中按鈕和圖標,進行組合。

          12. 選擇該組合,設置它的交互事件“鼠標單擊時”,但是很容易發現無法設置它跳轉到哪個頁面。因為這個組合代表九個格子,所以理論上應該設置跳轉到9個子頁面。然后需要利用中繼器的屬性進行設置。點擊column0,然后點擊“左側添加列”,生成Column1這一列。

          13. 選中Column1的第一行,然后右鍵點擊“引用頁面”,然后選擇第一個格子對應的子頁面。同理設置下面8個行。

          14. 設置該組合的交互事件“鼠標單擊時”,添加動作“鏈接-打開鏈接-當前窗口”,組織動作“選中鏈接到url或文件”,點擊“fx”打開“編輯值”彈窗,然后點擊“插入變量或函數”,然后選中“中繼器/數據集-Column1”,最后確定。

          15. 生成原型HTML并在瀏覽器中查看效果。

          三、添加到APP功能庫

          不同場景下的九宮格功能,標題不一樣,樣式相對固定。

          根據多年PM經驗,總結出2種常用的“九宮格”,添加到APP元件庫,供后續調用。

          九宮格

          十六宮格

          本文章中所有內容僅供學習交流使用,不用于其他任何目的,不提供完整代碼,抓包內容、敏感網址、數據接口等均已做脫敏處理,嚴禁用于商業用途和非法用途,否則由此產生的一切后果均與作者無關!

          本文章未經許可禁止轉載,禁止任何修改后二次傳播,擅自使用本文講解的技術而導致的任何意外,作者均不負責,若有侵權,請在公眾號【K哥爬蟲】聯系作者立即刪除!

          目標

          目標:百 X 網數字九宮格驗證碼逆向分析

          網址:aHR0cHM6Ly9iZWlqaW5nLmJhaXhpbmcuY29tL296L3M5dmVyaWZ5X2h0bWw=

          抓包分析

          本例中的驗證碼不是很難,但網站埋了點兒坑,容易出現識別正確、參數也正確,但仍然請求不成功的情況。訪問主頁響應碼為 307,接著請求了一個 bf.js 和兩個 s.webp 的圖片,然后又跳轉到首頁出現驗證碼。如果你沒有以上步驟,請求主頁直接就是 200 出現驗證碼,則需要清除 cookie 后再訪問,因為第一次 307 到請求 bf.js 再到兩次 s.webp 都是在設置 cookie。

          第一次請求主頁,response headers 會設置一個名為 _trackId__city 的 cookie,如下圖所示:

          然后帶著這兩個 cookie 請求了一個 bf.js,這個 js 用于后續兩個 s.webp 請求參數的加密,這里注意,第一個坑,雖然可以直接調試 js 扣算法下來后面直接用就行了,但是這個 js 必須得請求一遍,不然后面請求主頁的時候一直是 307。

          然后請求第一個 s.webp,get 請求,有三個參數:cfsf,明顯是加密得來的,同時請求的 cookie 也多了三個值:c0fc276cce08ba22dcc1fc276cce08ba22dcbxf,如下圖所示:

          然后請求第二個 s.webp,和第一個類似,get 請求也有三個參數:cfsf,cookie 和第一個一樣,但第二次請求返回了一個名為 sbxf 的新 cookie,其值和 bxfc1fc276cce08ba22dc 其實是一樣的,如下圖所示:

          然后帶上 __trackId__cityc0fc276cce08ba22dcc1fc276cce08ba22dcbxfsbxf 這六個 cookie 再次訪問主頁,就是驗證碼頁面了,返回的 html 里有個新的 js,很長一串,如下圖所示:

          然后觀察這個 js,里面包含了驗證碼圖片的 URL,以及需要點擊的數字,如下圖所示:

          點擊驗證后,會給 verify_url 發一個 get 請求,請求參數主要有一個 data,即點擊坐標(這個坐標也有講究,有可能你的值是對的,但有時候也不成功,這個后文再細說),cookie 和前面的請求一樣,如果驗證成功,會返回 ret 為 0,且有一個 code 供后續請求使用,如下圖所示:

          獲取 cookie

          這里再注意一點,所有的請求,header 只需要 RefererUser-Agent 就行了,不要亂加,比如多了個 Host 也有可能導致后續請求不成功。

          想要拿驗證碼,得先搞定 cookie,總體流程如下:

          1. 請求首頁 s9verify_html 獲取 __trackId__city,主要是 __trackId__city 要不要都行;
          2. 請求 bf.js,這一步不干啥,但必須得請求,不然 cookie 不能用;
          3. 第一次請求 s.webp,cookie 里多了 c0fc276cce08ba22dcc1fc276cce08ba22dcbxf,均為 js 生成;
          4. 第二次請求 s.webp,返回的 cookie 里多了 sbxf,其值和 bxf 一樣,這一步可以理解為激活 cookie,使其有效。

          前兩步倒沒有啥,第 3、4 步都有加密參數 sf,觀察這兩個 s.webp 都是 fetch 請求,所以我們直接一個 fetch 斷點,斷下后可以看到 cb 就是我們需要的兩個參數:

          觀察 bf.js 是一個小小的類似 OB 混淆,可以 AST 解一下混淆,但這個邏輯不是很復雜,所以直接硬看也行,關鍵語句 cb=c3['s'](c7, c8),c7 是定值一個字符串 fc276cce08ba22dc,c8 也是定值表示顏色的字符串 rgba(255, 0, 0, 255)

          主要是 c3['s']() 這個方法,跟進去,首先會取一下 c0fc276cce08ba22dcc1fc276cce08ba22dcbxf 三個值,如果有的話,直接返回,如果沒有的話,會生成新的,生成方法主要是 c6 這個函數,如下圖所示:

          繼續跟到 c6 方法里,首先對字符串 rgba(255, 0, 0, 255) 做了一個操作,生成了一張圖片的 base64 字符串:

          這里其實很明顯是 canvas 繪圖的一些操作,跟到 c7 看看確實是這樣的:

          這里對于我們扣算法來說,其實就不需要管了,因為同一臺設備的同一個瀏覽器,按照相同的規則繪制的圖片,base64 值是一樣的,所以我們直接忽略 c7 這個方法,直接把生成的 base64 值拿來用就行了。

          然后又將 base64 值進行了一個 c3["hash"]() 的操作,根據最終的值,或者跟到方法里去看,很容易發現這個其實就是個 MD5 的操作:

          接著往下看,八個字符串為一組,將 md5 值分為四組,然后四組之間用 0 或者 1 連接,拼接成新的 35 位字符串,拼接的是 0 還是 1,取決于中間的三目語句,判斷是否為 true,支持情況下都是 true,所以扣算法的話根本就沒必要再跟進去看是怎么判斷的,直接用 1 拼接就完事兒了。然后將固定的字符串 fc276cce08ba22dc 和這 35 位字符串拼接起來再一次 MD5,就得到了參數 s 的值,而參數 f 的值則是這個 35 位字符串。

          第一個 s.webp fetch 操作就完成了,接著是第二個 s.webp,就在第一個 fetch 附近,如下圖的 ce 就是第二次的 s、f 參數的值:

          這里生成的方法大致是一樣的,首先 cd 是一個新的圖片的 base64 值,這個值是第一次 s.webp 請求成功返回的,先把這個新的 base64 MD5 加密一下,生成一個新的字符串,相當于替換了第一次請求固定的字符串 fc276cce08ba22dc,后續的流程和第一次都一樣了:

          這兩次生成 s 和 f 的值的流程可以精簡成以下 js 實現:

          MD5=require("md5")
          
          var baseImg="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAANwAAADcCAYAAAAbWs+BAAAAAXNSR0IArs4c6QAAIABJREFUeF7tnQm..."
          
          function getParams(c8) {
              var cb=MD5(baseImg)
                , cc=cb.substring(0, 8)
                , cd=cb.substring(8, 16)
                , ce=cb.substring(16, 24)
                , cf=cb.substring(24, 32)
                , cg=cc + 1 + cd + 1 + ce + 1 + cf;
              return {
                  "s": MD5(c8 + cg),
                  "f": cg
              };
          }
          
          
          function getFirstParams() {
              return getParams("fc276cce08ba22dc")
          }
          
          function getSecondParams(img) {
              return getParams(MD5(img))
          }
          
          
          console.log(getFirstParams())
          console.log(getSecondParams("data:image/png;base64,dqyixSOIJuJN0IRG288itylhqNFFXVqL"))

          然后這個 cookie 值,你可以去 Hook 一下看看,但實際上觀察一下就可以發現 c0fc276cce08ba22dc 就是第一次 s.webp 請求的 s 參數,c1fc276cce08ba22dcbxf 就是第一次 s.webp 請求的 f 參數,所以直接拿來用就行了。

          獲取驗證碼

          帶上前面生成的正確的 cookie,再次請求主頁,響應碼為 200,然后在返回的 html 里可以看到有個超長的 js 地址,這個 js 直接把 .js 替換成 .jpg 就是驗證碼地址,替換成 .valid 就是驗證結果的地址,這個 js 返回的內容里面就包含了要點擊的數字。

          獲取點擊坐標

          最終提交的坐標是長這樣的:

          由于這個圖片是九宮格的樣式,一般的識別都是一排,所以這里可以將九宮格裁剪后重新排列一下(當然自己會搞深度學習的話可以單獨給這種九宮格訓練一下,就不用重新裁剪排列了),重新排列前后對比如下:

          這一步的利用 Python 的 PIL 庫很容易實現:

          from PIL import Image
          
          # 打開九宮格驗證碼
          captcha=Image.open("captcha.jpg")
          
          # 將圖片等分成三份,每份長寬為 150px 和 50px
          part1=captcha.crop((0, 0, 150, 50))
          part2=captcha.crop((0, 50, 150, 100))
          part3=captcha.crop((0, 100, 150, 150))
          part1.save("part1.jpg")
          part2.save("part2.jpg")
          part3.save("part3.jpg")
          
          # 創建新的圖片,長寬為 450px 和 50px
          new_captcha=Image.new("RGB", (450, 50))
          
          # 將三份圖片按順序拼接到新的圖片上
          new_captcha.paste(part1, (0, 0))
          new_captcha.paste(part2, (150, 0))
          new_captcha.paste(part3, (300, 0))
          
          # 保存新的圖片
          new_captcha.save("captcha_new.jpg")

          這樣處理后,怎樣得到對應的坐標呢?以上圖為例,假設我們需要點擊 question=[1, 8, 3, 6],我們識別 captcha_new.jpg 結果為 recognition_result="172958643",生成最后的坐標流程如下:

          import random
          
          
          question=[1, 8, 3, 6]           # 要點擊的數字
          recognition_result="172958643"  # captcha_new.jpg 識別的結果
          
          mapping_table={
              "0": f"{str(random.randint(15, 35))},{str(random.randint(15, 35))}|",
              "1": f"{str(random.randint(65, 85))},{str(random.randint(15, 35))}|",
              "2": f"{str(random.randint(115, 135))},{str(random.randint(15, 35))}|",
          
              "3": f"{str(random.randint(15, 35))},{str(random.randint(65, 85))}|",
              "4": f"{str(random.randint(65, 85))},{str(random.randint(65, 85))}|",
              "5": f"{str(random.randint(115, 135))},{str(random.randint(65, 85))}|",
          
              "6": f"{str(random.randint(15, 35))},{str(random.randint(115, 135))}|",
              "7": f"{str(random.randint(65, 85))},{str(random.randint(115, 135))}|",
              "8": f"{str(random.randint(115, 135))},{str(random.randint(115, 135))}|",
          }
          
          
          answer=""
          for q in question:
              for r in recognition_result:
                  if q==int(r):
                      answer +=mapping_table[str(recognition_result.index(r))]
          
          print(answer)

          每一個數字的圖片大小是 50x50,如果我要點擊上圖中的數字 1,那么我的 x、y 坐標范圍就應該為 [0~50, 0~50],如果我要點擊上圖中的數字 8,那么我的 x、y 坐標范圍就應該為 [100~150, 50~100]

          但是進過多次測試,點擊區域要靠正中心一點,成功率才高,所以坐標范圍前后各增加、減少了 15。對應數字 1 的坐標范圍就應該是 [15~35, 15~35],數字 8 的坐標范圍就應該是 [115~135, 65~85]

          這里為了簡便,直接定義了一個映射表 mapping_table,如果我點擊數字 8,那么 captcha_new.jpg 識別結果 172958643 中,8 的位置是 5,對應 mapping_table["5"],也就是 random.randint(115, 135), str(random.randint(65, 85)

          結果驗證

          常在某些 app 中看到這樣的九宮格設計。當縮略圖不足 9 張時,正常排列,當超過 9 張時,會提示還剩多少張,如下:

          如何使用純 CSS 實現這一效果呢?一起來看看吧

          一、九宮格布局

          布局就很簡單了,一個很普通的九宮格布局,這里使用 grid

          <ul class="list">
            <li class="item"></li>
            <li class="item"></li>
            <li class="item"></li>
              ...
          </ul>

          這里正方形可以用aspect-ratio[1]簡易實現,對應的 CSS 如下

          .list{
            position: relative;
            display: grid;
            width: 300px;
            margin: auto;
            grid-template-columns: repeat(3,1fr);
            list-style: none;
            padding: 0;
            gap: 2px;
          }
          .item{
            aspect-ratio: 1;/*寬高比1:1*/
          }

          效果如下

          那么,如何實現在超過9張時自動提示剩余張數呢?接著往下看

          二、CSS 計數器

          提到序列,自然會想到 CSS 計數器[2],現在我們加上計數器

          .list{
            /*...*/
            counter-reset: count; /*初始化*/
          }

          然后在每一個 .item顯示數字,可以用到偽元素::after

          .item{
            counter-increment: count;
          }
          .item::after{
            content: counter(count);
            /*其他樣式*/
            display: grid;
            height: 100%;
            place-content: center;
            font-size: 30px;
            color: #fff;
          }

          這樣可以得到如下效果

          數字是顯示出來了,不過現在還有兩個問題:

          1.數量超過9個時,不會隱藏超過的圖片2.這個數字不是超出圖片的數量,而是總數

          三、 隱藏超出的圖片

          這個其實非常容易,由于數量是固定的,只需要利用選擇器nth-child配合~就能實現

          .item:nth-child(9)~.item{
            /*選擇第9個以后的元素*/
            visibility: hidden;
          }

          這個地方是通過 visibility: hidden隱藏超過的圖片,原因是該屬性不會影響計數器的計算,如果使用display:none則會跳過計數

          四、 統計超過的數量

          目前由于是從第1個開始計數,所以最后統計的是整個列表的數量,但是我們可以指定從第10個才開始計數,會得到什么效果呢?為了方便演示,暫時把隱藏打開

          .item{
            /*counter-increment: count;*/
          }
          .item:nth-child(9)~.item{
            /*從第10個開始計數*/
            counter-increment: count;
          }
          .item:nth-child(9)~.item::after{
            content: counter(count);
          } 

          可以看到,從第10個開始計數后,最后一個數字就表示還剩余多少張

          現在把最后一張放在右下角就行了(絕對定位),最后一張可以用.item:nth-child(9)~.item:last-child來選擇,表示第9張后面的最后一張圖片,實現如下

          .item:nth-child(9)~.item{
            position: absolute;
            width: calc(100% / 3 - 1px);
            counter-increment: count;
            visibility: hidden;
            right: 0;
            bottom: 0;
          }
          .item:nth-child(9)~.item:last-child::after{
            visibility: visible;
            background-color: rgba(0,0,0,.2);
          }

          這樣就實現了純 CSS 自動提示剩余圖片的效果,演示如下

          這里的 addremove 是演示動態修改節點數量,與交互邏輯無關

          完整代碼可訪問 list-counter (codepen.io)[3]

          五、其他初始化方式

          在上一種實現方式中,我們是手動指定從第 10 個元素開始計數的

          .item:nth-child(9)~.item{
            /*從第10個開始計數*/
            counter-increment: count;
          }

          其實,還有一種方式也值得一試,那就是直接指定計數器的初始值,默認為0,現在改為 -9 就可以了,實現如下

          .list{
            /*...*/
            counter-reset: count -9; /*初始化為-9*/
          }

          不一樣的初始化思路,剩下的就和之前一樣的邏輯了,完整代碼可訪問 list-counter-reset (codepen.io)[4]

          六、總結和說明

          這個案例到這里就結束了,一個低成本的 CSS 小技巧,雖然不多,但是非常實用,尤其是選擇器的運用,說不定將來哪次就會用上了。CSS 計數器可以說是非常靈活和強大了,仔細挖掘應該還能實現更多實用的效果,這里總結一下:

          1. 九宮格布局如果不考慮兼容性優先使用 grid 布局
          2. 自適應正方形可以使用 aspect-ratio 實現
          3. 遇到和序列有關的布局,優先考慮 CSS 計數器
          4. 靈活運用 CSS 選擇器,nth-child(n)~ 可以組合選擇第 n 個以后的元素
          5. 可以指定從第 n 個元素開始計數
          6. 可以指定計數器的初始值
          7. CSS 計數器沒有兼容性問題,可以放心使用

          如果覺得還不錯,對你有幫助的話,歡迎點贊、收藏、轉發???

          References

          [1] aspect-ratio: https://www.zhangxinxu.com/wordpress/2021/02/css-aspect-ratio/
          [2] CSS 計數器:
          https://developer.mozilla.org/zh-CN/docs/Web/CSS/CSS_Lists_and_Counters/Using_CSS_counters
          [3] list-counter (codepen.io):
          https://codepen.io/xboxyan/pen/NWjroNz
          [4] list-counter-reset (codepen.io):
          https://codepen.io/xboxyan/pen/rNmWxyp


          主站蜘蛛池模板: 伊人久久大香线蕉AV一区二区| 国产一区二区女内射| 亚洲AV无码一区二区三区网址| 国产精品高清一区二区三区| 97久久精品无码一区二区| 成人免费视频一区二区三区| 日本一区二区在线播放| 亚洲国产精品一区二区第一页| 精品一区二区三区免费| 亚洲av无码一区二区三区四区| 国产suv精品一区二区6| 精品日本一区二区三区在线观看| 国产色情一区二区三区在线播放| 国产伦精品一区二区三区视频小说| 亚洲AV永久无码精品一区二区国产 | 久久精品一区二区免费看| 无码人妻精品一区二区三区9厂| 国产麻豆媒一区一区二区三区| 91大神在线精品视频一区| 视频在线观看一区二区三区| 91在线一区二区三区| 天堂资源中文最新版在线一区| 亚洲av日韩综合一区在线观看| 99精品国产高清一区二区麻豆 | 亚洲一区二区三区四区在线观看| 无码人妻久久一区二区三区| 日韩免费一区二区三区在线 | 日本美女一区二区三区 | 麻豆一区二区99久久久久| 中文字幕人妻无码一区二区三区| 精品久久国产一区二区三区香蕉 | 91福利国产在线观一区二区| 精品福利一区二区三| 亚洲sm另类一区二区三区| 成人日韩熟女高清视频一区| 国产av夜夜欢一区二区三区| 合区精品久久久中文字幕一区| 亚洲av永久无码一区二区三区| 亚洲国产一区二区a毛片| 亚洲国产一区视频| 国产一区二区三区美女|