feat: enhance image handling for weather arts
- Remove deprecated `image_with_watermark` attachment. - Introduce `webp_image`, `preview_image`, and `watermarked_image` methods in `WeatherArt` model for optimized image formats. - Update views to use new image variants, including webp and previews, improving loading times and visual quality. - Ensure images are processed with relevant attributes such as quality and dimensions for better performance and user experience. These changes enhance the image handling capabilities of the application by ensuring images are served in a more efficient format (WebP) and with improved resizing options, leading to better performance overall.
This commit is contained in:
parent
5efe441fa8
commit
5ae0367525
@ -42,14 +42,14 @@ class City < ApplicationRecord
|
||||
# 根据数据库类型构建不同的查询
|
||||
base_query = if ActiveRecord::Base.connection.adapter_name.downcase == "sqlite"
|
||||
joins(<<-SQL.squish)
|
||||
LEFT JOIN ahoy_events ON
|
||||
LEFT JOIN ahoy_events ON#{' '}
|
||||
json_extract(ahoy_events.properties, '$.city_id') = cities.id
|
||||
AND json_extract(ahoy_events.properties, '$.event_type') = 'city_view'
|
||||
AND ahoy_events.time > '#{start_time}'
|
||||
SQL
|
||||
else
|
||||
joins(<<-SQL.squish)
|
||||
LEFT JOIN ahoy_events ON
|
||||
LEFT JOIN ahoy_events ON#{' '}
|
||||
(ahoy_events.properties::jsonb->>'city_id')::integer = cities.id
|
||||
AND ahoy_events.properties::jsonb->>'event_type' = 'city_view'
|
||||
AND ahoy_events.time > '#{start_time}'
|
||||
|
@ -4,7 +4,6 @@ class WeatherArt < ApplicationRecord
|
||||
|
||||
belongs_to :city
|
||||
has_one_attached :image
|
||||
has_one_attached :image_with_watermark
|
||||
|
||||
has_many :visits, class_name: "Ahoy::Visit", foreign_key: :weather_art_id
|
||||
has_many :events, class_name: "Ahoy::Event", foreign_key: :weather_art_id
|
||||
@ -59,4 +58,145 @@ class WeatherArt < ApplicationRecord
|
||||
def image_url
|
||||
image.attached? ? image.blob : nil
|
||||
end
|
||||
|
||||
def webp_image
|
||||
return nil unless image.attached?
|
||||
|
||||
image.variant(
|
||||
format: "webp",
|
||||
saver: {
|
||||
quality: 100,
|
||||
strip: true, # 移除元数据以减小文件大小
|
||||
interlace: "plane" # 渐进式加载
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
# 添加图片变体处理
|
||||
PREVIEW_DIMENSIONS = {
|
||||
big: [ 1792, 1024 ],
|
||||
medium: [ 896, 512 ],
|
||||
small: [ 448, 256 ]
|
||||
}.freeze
|
||||
def preview_image(size = :medium)
|
||||
return nil unless image.attached?
|
||||
|
||||
width, height = PREVIEW_DIMENSIONS[size] || PREVIEW_DIMENSIONS[:medium]
|
||||
|
||||
image.variant(
|
||||
resize_to_limit: [ width, height ],
|
||||
format: "webp",
|
||||
saver: {
|
||||
quality: 75,
|
||||
strip: true, # 移除元数据以减小文件大小
|
||||
interlace: "plane" # 渐进式加载
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
def watermarked_image
|
||||
return nil unless image.attached?
|
||||
|
||||
overlay_text = create_overlay_text
|
||||
|
||||
image.variant(
|
||||
composite: [ {
|
||||
input: overlay_text,
|
||||
gravity: "southeast"
|
||||
} ]
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_overlay_text
|
||||
{
|
||||
create: {
|
||||
width: 400,
|
||||
height: 100,
|
||||
background: [ 0, 0, 0, 0.5 ] # 半透明黑色背景
|
||||
},
|
||||
"svg-overlay": %(
|
||||
<svg width="400" height="100">
|
||||
<text x="20" y="40"
|
||||
style="fill: white; font-family: Arial; font-size: 20px;">
|
||||
#{city.name} - #{weather_date.strftime('%Y-%m-%d')}
|
||||
</text>
|
||||
<text x="20" y="70"
|
||||
style="fill: white; font-family: Arial; font-size: 20px;">
|
||||
© todayaiweather.com
|
||||
</text>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
end
|
||||
|
||||
def create_text_layer(font_size)
|
||||
text = [
|
||||
weather_date.strftime("%Y-%m-%d"),
|
||||
"#{temperature}°C, #{description}",
|
||||
"#{city.name}, #{city.country.name}, #{city.country.region.name}",
|
||||
"© todayaiweather.com"
|
||||
].join("\n")
|
||||
|
||||
{
|
||||
create: {
|
||||
width: 600,
|
||||
height: 200,
|
||||
background: [ 0, 0, 0, 0 ]
|
||||
},
|
||||
"svg-overlay": %(
|
||||
<svg width="600" height="200">
|
||||
<style>
|
||||
.text {
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: #{font_size}px;
|
||||
}
|
||||
.shadow {
|
||||
fill: white;
|
||||
stroke: black;
|
||||
stroke-width: 2px;
|
||||
paint-order: stroke fill;
|
||||
}
|
||||
</style>
|
||||
<text x="20" y="#{font_size + 10}" class="text shadow">#{weather_date.strftime('%Y-%m-%d')}</text>
|
||||
<text x="20" y="#{font_size * 2 + 20}" class="text shadow">#{temperature}°C, #{description}</text>
|
||||
<text x="20" y="#{font_size * 3 + 30}" class="text shadow">#{city.name}, #{city.country.name}</text>
|
||||
<text x="20" y="#{font_size * 4 + 40}" class="text shadow">© todayaiweather.com</text>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
end
|
||||
|
||||
def watermark_command(font_size:, stroke_width:, spacing:)
|
||||
date_str = weather_date.strftime("%Y-%m-%d")
|
||||
weather_info = "#{temperature}°C, #{description}"
|
||||
location_info = "#{city.name}, #{city.country.name}, #{city.country.region.name}"
|
||||
copyright = "© todayaiweather.com"
|
||||
|
||||
"gravity southeast " \
|
||||
"fill white " \
|
||||
"font Arial " \
|
||||
"pointsize #{font_size} " \
|
||||
"stroke black " \
|
||||
"strokewidth #{stroke_width} " \
|
||||
"text 30,#{spacing * 12} '#{copyright}' " \
|
||||
"text 30,#{spacing * 8} '#{location_info}' " \
|
||||
"text 30,#{spacing * 4} '#{weather_info}' " \
|
||||
"text 30,#{spacing} '#{date_str}'"
|
||||
end
|
||||
|
||||
def watermark_text
|
||||
date_str = weather_date.strftime("%Y-%m-%d")
|
||||
weather_info = "#{temperature}°C, #{description}"
|
||||
location_info = "#{city.name}, #{city.country.name}, #{city.country.region.name}"
|
||||
copyright = "© todayaiweather.com"
|
||||
|
||||
[
|
||||
"text 30,120 '#{copyright}'",
|
||||
"text 30,80 '#{location_info}'",
|
||||
"text 30,40 '#{weather_info}'",
|
||||
"text 30,0 '#{date_str}'"
|
||||
].join(" ")
|
||||
end
|
||||
end
|
||||
|
@ -6,7 +6,7 @@
|
||||
<!-- 背景图像 -->
|
||||
<% if featured_art&.image&.attached? %>
|
||||
<div class="absolute inset-0 h-[40vh] overflow-hidden">
|
||||
<%= image_tag featured_art.image,
|
||||
<%= image_tag featured_art.webp_image.processed,
|
||||
class: "w-full h-full object-cover" %>
|
||||
<div class="absolute inset-0 bg-gradient-to-b from-base-100/30 via-base-100/60 to-base-100"></div>
|
||||
</div>
|
||||
@ -107,7 +107,7 @@
|
||||
<div class="card bg-base-100 shadow-xl hover:shadow-2xl transition-all duration-300 group overflow-hidden">
|
||||
<figure class="relative aspect-square overflow-hidden">
|
||||
<% if art.image.attached? %>
|
||||
<%= image_tag art.image,
|
||||
<%= image_tag art.preview_image.processed,
|
||||
class: "w-full h-full object-cover transform group-hover:scale-105 transition-transform duration-500" %>
|
||||
|
||||
<div class="absolute inset-0 bg-gradient-to-t from-black via-black/50 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
|
||||
|
@ -5,7 +5,7 @@
|
||||
<div class="relative">
|
||||
<!-- 图片 -->
|
||||
<figure class="aspect-[16/9] overflow-hidden">
|
||||
<%= image_tag city.latest_weather_art.image,
|
||||
<%= image_tag city.latest_weather_art.preview_image.processed,
|
||||
class: "w-full h-full object-cover transform group-hover:scale-105 transition-transform duration-500" %>
|
||||
</figure>
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
<!-- 背景图像和渐变 -->
|
||||
<% if featured_art&.image&.attached? %>
|
||||
<div class="absolute inset-0 h-[60vh] overflow-hidden">
|
||||
<%= image_tag featured_art.image,
|
||||
<%= image_tag featured_art.webp_image.processed,
|
||||
class: "w-full h-full object-cover object-center" %>
|
||||
<div class="absolute inset-0 bg-gradient-to-b from-base-100/40 via-base-100/80 to-base-100"></div>
|
||||
</div>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<!-- 背景效果 -->
|
||||
<% if @city.latest_weather_art&.image&.attached? %>
|
||||
<div class="fixed inset-0 -z-10">
|
||||
<%= image_tag @city.latest_weather_art.image,
|
||||
<%= image_tag @city.latest_weather_art.webp_image.processed,
|
||||
class: "absolute w-full h-full object-cover scale-110 filter blur-2xl opacity-25" %>
|
||||
<div class="absolute inset-0 bg-gradient-to-b from-base-200/90 to-base-200/70 backdrop-blur-md"></div>
|
||||
</div>
|
||||
@ -103,7 +103,7 @@
|
||||
<div class="card bg-base-100/80 backdrop-blur-sm shadow-xl hover:shadow-2xl transition-all duration-300 hover:-translate-y-1">
|
||||
<figure class="relative aspect-video overflow-hidden">
|
||||
<% if art.image.attached? %>
|
||||
<%= image_tag art.image,
|
||||
<%= image_tag art.preview_image.processed,
|
||||
class: "w-full h-full object-cover transform hover:scale-105 transition-transform duration-500" %>
|
||||
<div class="absolute bottom-0 left-0 right-0 p-4 bg-gradient-to-t from-black/70 to-transparent">
|
||||
<div class="flex items-center justify-between text-white">
|
||||
|
@ -27,15 +27,15 @@
|
||||
<figure class="relative lg:h-[30rem] h-auto overflow-hidden rounded-lg"> <!-- 添加圆角 -->
|
||||
<div class="gallery" data-controller="photo-swipe-lightbox">
|
||||
<div data-photo-swipe-lightbox-target="gallery" class="h-full">
|
||||
<% blob = @weather_art.image_blob %>
|
||||
<%= link_to rails_blob_path(blob),
|
||||
<% watermarked = @weather_art.webp_image.processed %>
|
||||
<%= link_to rails_blob_path(watermarked),
|
||||
data: {
|
||||
pswp_src: rails_blob_url(blob),
|
||||
pswp_src: rails_blob_url(watermarked),
|
||||
pswp_caption: 'Weather Art',
|
||||
pswp_width: 1792,
|
||||
pswp_height: 1024
|
||||
} do %>
|
||||
<%= image_tag @weather_art.image, class: "object-cover w-full h-full transition-transform transform hover:scale-105 ease-in-out" %>
|
||||
<%= image_tag @weather_art.preview_image(:big).processed, class: "object-cover w-full h-full transition-transform transform hover:scale-105 ease-in-out" %>
|
||||
<%#= image_tag @weather_art.watermarked_variant.processed , class: "object-cover w-full h-full transition-transform transform hover:scale-105 ease-in-out" %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user