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:
songtianlun 2025-02-13 15:25:12 +08:00
parent 5efe441fa8
commit 5ae0367525
7 changed files with 153 additions and 13 deletions

View File

@ -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}'

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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">

View File

@ -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>