需求背景

我们可以看到很多富有生活气息的独立站或者平台,都在使用这个功能,卖家可以在图片上添加按钮,将图片中的产品设置为可购买的。在这个功能的帮助下,使用富有生活气息的图片可以激发买家的购物欲望,当你的一张图片中某个产品吸引了买家的眼球,买家只需点击几下鼠标就可能将购物欲望变成了转化。
现存一些主题很多是不包含此功能的,那么这个功能加起来到底是不是那么麻烦呢?带着这些疑问我们往下看。

1. 实现原理

微信图片编辑_20220414115132.jpg
底层box将图片和商品标记框包裹起来,例如图中p1, 相对box进行绝对定位。点击商品标记框,加载出商品信息并进行展示。

需要注意的细节:

  1. 要想图片展示最佳的效果,图片展示的大小需要靠图片自身进行填充。因此商品标记框的位置也许要根据比例来进行设置才不会变。
  2. 点击之后商品信息加载显示,这个地方是比较容易踩坑的地方。

    博主现在看到很多的弹层是在设置商品的时候,直接将商品数据渲染到热点商品位置了。这种形式不是说不好,现在比如只有一个这个组件,是ok的。但是有10个或者更多的话,商品数据加载多了会间接的影响dom加载的大小,并且在后端all_products取商品的时候也会有一定的影响。因此比较推荐的是使用异步的方式进行加载,加载出来的数据加上标记,不需要重复进行加载。效果几乎和初始的时候加载数据差不多,但是避免了那些缺点。

    2. 如何实现

按照我们的实现原理只需要添加3个文件就可以实现此功能。并且这个实现和主题无关。
目前图片未添加懒加载支持,商品的quick view功能,因为不同主题的实现方式不同。笔者只是抛砖引玉,至于能做成啥样的,还是得看大家的脑洞了。
Section : shoppable-image.liquid , shoppable-ajax-product.liquid
Snippets: shoppable-image-product.liquid

  1. {%- capture hotspot_products -%}
  2. {%- for block in section.blocks -%}
  3. {%- if block.settings.product != blank -%}
  4. {%- render 'shoppable-image-product',
  5. type: 'component',
  6. productHandle: block.settings.product,
  7. top: block.settings.top,
  8. left: block.settings.left
  9. -%}
  10. {%- endif -%}
  11. {%- endfor -%}
  12. {%- endcapture -%}
  13. {%- capture shoppable_id -%}shoppable-{{ section.id }}{%- endcapture -%}
  14. <div class="shoppable-container" id="{{ shoppable_id }}">
  15. {%- if section.settings.title != blank -%}
  16. <div class="shoppable-head">
  17. {{ section.settings.title }}
  18. </div>
  19. {%- endif -%}
  20. <div class="shoppable-body">
  21. <div class="shoppable-body__image">
  22. <img src="{{ section.settings.image | img_url: 'master' }}" />
  23. </div>
  24. <div class="shoppable-body__products">
  25. {{ hotspot_products }}
  26. </div>
  27. </div>
  28. </div>
  29. <style>
  30. #{{ shoppable_id }} .shoppable-head {
  31. text-align: center;
  32. margin: 10px 0;
  33. font-size: 20px;
  34. font-weight: bold;
  35. }
  36. #{{ shoppable_id }} .shoppable-body{
  37. position: relative;
  38. }
  39. #{{ shoppable_id }} .shoppable-body .shoppable-body__image {
  40. width: 100%;
  41. }
  42. #{{ shoppable_id }} .shoppable-body .shoppable-body__image img {
  43. width: 100%;
  44. }
  45. #{{ shoppable_id }} .shoppable-body .shoppable-body__products {
  46. position: absolute;
  47. top: 0;
  48. left: 0;
  49. width: 100%;
  50. height: 100%;
  51. }
  52. #{{ shoppable_id }} .shoppable-body .shoppable-body__products .product-data {
  53. position: absolute;
  54. width: 30px;
  55. height: 30px;
  56. cursor: pointer;
  57. background-color: #0006;
  58. border-radius: 50%;
  59. outline: none;
  60. box-shadow: none;
  61. transition: background-color .15s cubic-bezier(.4,0,.2,1),box-shadow .15s cubic-bezier(.4,0,.2,1);
  62. }
  63. #{{ shoppable_id }} .shoppable-body .shoppable-body__products .product-data:hover {
  64. z-index: 299;
  65. box-sizing: border-box;
  66. box-shadow: 0 0 0 2px #fff inset, 0 1px 4px #00000026;
  67. }
  68. #{{ shoppable_id }} .shoppable-body .shoppable-body__products .product-data .product-data__card{
  69. display: none;
  70. }
  71. #{{ shoppable_id }} .shoppable-body .shoppable-body__products .product-data:hover .product-data__card {
  72. display: block;
  73. width: 200px;
  74. background: #fff;
  75. font-size: 12px;
  76. position: absolute;
  77. transform: translate( -50% ,30px);
  78. box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
  79. }
  80. #{{ shoppable_id }} .shoppable-body .shoppable-body__products .product-data__pointer {
  81. position: absolute;
  82. display: block;
  83. bottom: 50%;
  84. left: 50%;
  85. width: 14px;
  86. height: 14px;
  87. cursor: pointer;
  88. background-color: #fff;
  89. border-radius: 50%;
  90. transition: transform .15s cubic-bezier(.4,0,.2,1);
  91. transform: translate(-50%,50%);
  92. }
  93. #{{ shoppable_id }} .view__detail {
  94. text-align: center;
  95. padding: 0 0 20px;
  96. }
  97. #{{ shoppable_id }} .view__image {
  98. display: flex;
  99. justify-content: center;
  100. }
  101. #{{ shoppable_id }} .view__detail .detail__title {
  102. color: #000;
  103. text-decoration: none;
  104. }
  105. #{{ shoppable_id }} .view__detail .btn-options {
  106. background: #000;
  107. padding: 5px;
  108. color: #fff;
  109. text-decoration: none;
  110. }
  111. </style>
  112. <script>
  113. var selector = '#{{ shoppable_id }} .product-data';
  114. function loadProduct(el) {
  115. if ($(el).hasClass('data-loaded')) {
  116. return;
  117. }
  118. var handle = $(el).attr('data-handle');
  119. fetch(`/products/${handle}?section_id=shoppable-ajax-product`)
  120. .then(function(response) {
  121. return response.text();
  122. }).then(function(html) {
  123. var nodeHtml = $(html).find('.data-wapper').html();
  124. $(el).find('.product-data__card').html(nodeHtml);
  125. $(el).addClass('data-loaded');
  126. });
  127. }
  128. $(selector).on('hover', function() {
  129. loadProduct(this);
  130. });
  131. $(document).ready(function() {
  132. $(selector).each(function(_, node) {
  133. loadProduct(node);
  134. });
  135. });
  136. </script>
  137. {% schema %}
  138. {
  139. "name": "Shoppable image",
  140. "class": "shoppable-image--section",
  141. "max_blocks": 5,
  142. "settings": [
  143. {
  144. "type": "text",
  145. "id": "title",
  146. "label": "标题",
  147. "default": "标题"
  148. },
  149. {
  150. "type": "image_picker",
  151. "id": "image",
  152. "label": "图片"
  153. }
  154. ],
  155. "blocks": [
  156. {
  157. "type": "hotspot",
  158. "name": "配置商品",
  159. "settings": [
  160. {
  161. "type": "product",
  162. "id": "product",
  163. "label": "Product"
  164. },
  165. {
  166. "type": "range",
  167. "id": "top",
  168. "label": "图片距box顶部偏移位置",
  169. "min": 0,
  170. "max": 100,
  171. "step": 1,
  172. "unit": "%",
  173. "default": 50
  174. },
  175. {
  176. "type": "range",
  177. "id": "left",
  178. "label": "图片距离box左侧偏移位置",
  179. "min": 0,
  180. "max": 100,
  181. "step": 1,
  182. "unit": "%",
  183. "default": 50
  184. }
  185. ]
  186. }
  187. ],
  188. "presets": [
  189. {
  190. "name": "Shoppable image",
  191. "category": "Image"
  192. }
  193. ]
  194. }
  195. {% endschema %}
  1. {%- render 'shoppable-image-product', product: product -%}
  1. {%- comment -%}
  2. 入参:
  3. type: ajax 通过ajax方式加载 / component 通过组件方式加载
  4. productHandle: 商品handle
  5. product: 商品数据
  6. top: 偏移box, top 位置百分比
  7. left: 偏移box, left位置百分比
  8. {%- endcomment -%}
  9. {%- capture shoppable_product -%}
  10. <div class="data-wapper">
  11. <div class="view__image">
  12. <a class="grid-view-item__link grid-view-item__image" href="{{ product.url | within: collection }}">
  13. <img src="{{ product.featured_image | img_url: '180x' }}" alt="">
  14. </a>
  15. </div>
  16. <div class="view__detail">
  17. <a href="{{ product.url | within: collection }}" class="detail__title">{{ product.title }}</a>
  18. <div class="detail__meta">
  19. {{ product.price | money_with_currency }}
  20. </div>
  21. <a class="btn-options" href="{{ product.url | within: collection }}">SELECT OPTIONS</a>
  22. </div>
  23. </div>
  24. {%- endcapture -%}
  25. {%- if type == 'component' -%}
  26. <div class="product-data" data-handle="{{ productHandle }}" style="position:absolute; left: {{ left }}%; top: {{ top }}%" >
  27. <div class="product-data__pointer"></div>
  28. <div class="product-data__card"></div>
  29. </div>
  30. {%- else -%}
  31. {{ shoppable_product }}
  32. {%- endif -%}

3. 展示效果

店铺地址: https://asen-practice.myshopify.com/ 密码:test

点击查看【bilibili】

Tips: 如有引用请标注源文章地址

感谢 高帅帅 同学对本期教程的赞助!

关注我的【小红书】,第一时间掌握更新动态


你的鼓励就是我创作的动力!
zanshang.png