Files
video_pro-rails7/modules/video_pro/_video_pro_widget8.html.erb

388 lines
9.6 KiB
ERB

<div class="w-video_data video_data-widget-8" data-subpart-id="{{subpart-id}}">
<h3 class="widget-title"><span>{{widget-title}}</span></h3>
<div class="vw3-carousel">
<button class="vw3-prev" title="上一組" aria-label="上一組" type="button">
<i class="fa fa-angle-left" aria-hidden="true"></i>
</button>
<div class="vw3-track-wrap">
<ul class="video_data_wrap vw3-track" data-list="videos" data-level="0">
<li class="video_data vw3-slide" data-video-url="{{video_url}}" data-video-webm-url="{{video_webm_url}}" data-type="{{video_type}}">
<a href="{{video_show_url}}" title="{{video_title_escape}}">
<div class="video_linkwrapper3">
<div class="videohover">
<div class="view_info"><span>{{view_count}}</span></div>
<div class="Video__Player"><button class="Video__PlayerButton" type="button"></button></div>
</div>
<img class="video_snapshot" src="{{snapshot_url}}" alt="{{video_title_escape}}">
</div>
</a>
<div class="video_info">
<div class="video_title">
<a class="video_link" href="{{video_show_url}}" title="{{video_title_escape}}"><h4>{{video_title}}</h4></a>
</div>
<div class="video_desc">
<a class="video_link" href="{{video_show_url}}" title="{{video_title_escape}}">{{video_desc}}</a>
</div>
</div>
</li>
</ul>
</div>
<button class="vw3-next" title="下一組" aria-label="下一組" type="button">
<i class="fa fa-angle-right" aria-hidden="true"></i>
</button>
</div>
</div>
<style>
.video_data-widget-8 {
position: relative;
.widget-title { margin-bottom: 1rem; }
/* 外層輪播框 */
.vw3-carousel {
display: flex;
align-items: center;
gap: 0.5rem;
position: relative;
}
/* 可視區域 */
.vw3-track-wrap {
overflow: hidden;
flex: 1;
}
/* 滑動軌道 */
.vw3-track {
display: flex;
align-items: center;
gap: 1rem;
list-style: none;
margin: 0;
padding: 0;
transition: transform 0.4s ease;
}
/* 所有 slide 共用 */
.vw3-slide {
flex-shrink: 0;
box-sizing: border-box;
}
/* 第一張大卡:40% 相對容器 */
.vw3-slide:nth-child(1) {
width: 40%;
position: relative;
.video_linkwrapper3 { margin-bottom: 0; }
.video_info {
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding: 2rem 1rem 1rem;
background: linear-gradient(to top, rgba(0,0,0,0.7) 0%, transparent 100%);
border-radius: 0 0 14px 14px;
z-index: 10;
margin: 0;
}
.video_title h4 {
color: #fff;
font-size: 1.15rem;
font-weight: 500;
margin: 0 0 0.2rem;
}
.video_desc,
.video_desc a {
color: rgba(255,255,255,0.85);
font-size: 0.85rem;
}
}
/* 後續小卡:各約 20% */
.vw3-slide:nth-child(n+2) {
width: calc(20% - 0.75rem);
.video_info {
padding: 0.5rem 0 0;
margin: 0;
}
.video_title h4 {
font-size: 0.95rem;
font-weight: 500;
color: #1a1a1a;
margin: 0 0 0.2rem;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.video_desc,
.video_desc a {
font-size: 0.82rem;
color: #666;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
}
/* 圖片共用 */
.video_linkwrapper3 {
position: relative;
aspect-ratio: 16 / 9;
overflow: hidden;
border-radius: 14px;
box-shadow: 0 2px 12px rgba(0,0,0,0.1);
transition: transform 0.22s ease, box-shadow 0.22s ease;
&:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(0,0,0,0.18);
}
img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
}
/* 播放按鈕 */
.Video__Player {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 10;
}
.Video__PlayerButton {
width: 48px;
height: 48px;
border-radius: 50%;
background: rgba(255,255,255,0.85);
border: none;
cursor: pointer;
position: relative;
&::after {
content: '';
position: absolute;
top: 50%;
left: 54%;
transform: translate(-50%, -50%);
width: 0;
height: 0;
border-style: solid;
border-width: 8px 0 8px 16px;
border-color: transparent transparent transparent #1a1a1a;
}
&:hover { background: #fff; }
}
/* 左右箭頭 */
.vw3-prev,
.vw3-next {
flex-shrink: 0;
width: 36px;
height: 36px;
border-radius: 50%;
background: rgba(0,0,0,0.15);
color: #fff;
border: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.2rem;
transition: background 0.2s;
z-index: 10;
&:hover { background: rgba(0,0,0,0.4); }
&:focus { outline: 2px solid #2980b9; outline-offset: 2px; }
&:disabled { opacity: 0.3; cursor: default; }
}
/* view_info */
.view_info {
position: absolute;
top: 10px;
left: 10px;
background: rgba(0,0,0,0.75);
color: #fff;
font-size: 0.8rem;
padding: 2px 8px;
border-radius: 3px;
z-index: 10;
display: flex;
align-items: center;
gap: 4px;
}
/* RWD */
@media(max-width: 768px) {
.vw3-slide:nth-child(1) { width: 100%; }
.vw3-slide:nth-child(n+2) { width: calc(50% - 0.5rem); }
}
@media(max-width: 480px) {
.vw3-slide:nth-child(n+2) { width: 100%; }
}
}
</style>
<script>
$(document).ready(function() {
var $widget = $('[data-subpart-id="{{subpart-id}}"]');
var $trackWrap = $widget.find('.vw3-track-wrap');
var $track = $widget.find('.vw3-track');
var $prevBtn = $widget.find('.vw3-prev');
var $nextBtn = $widget.find('.vw3-next');
var originalSlides = $widget.find('.vw3-slide').toArray();
var total = originalSlides.length;
if (total === 0) return;
var currentIndex = 0; // 目前第一個顯示的 slide index(在 originalSlides 裡)
// 重建 track:把所有 slide 克隆三份(前後各補一輪),讓循環無縫
function buildTrack() {
$track.empty();
// 補後一輪(讓往右循環)
$(originalSlides).each(function() {
$track.append($(this).clone().addClass('vw3-clone'));
});
// 原始
$(originalSlides).each(function() {
$track.append($(this).clone());
});
// 補前一輪(讓往左循環)
$(originalSlides).each(function() {
$track.append($(this).clone().addClass('vw3-clone'));
});
}
function getVisibleCount() {
return $(window).width() < 1024 ? 1 : 4;
}
function isMobile() {
return $(window).width() < 1024;
}
// 更新每個 slide 的寬度
function updateWidths() {
var $allSlides = $track.find('.vw3-slide');
var wrapWidth = $trackWrap.width();
var gap = 16;
var visibleCount = getVisibleCount();
var mobile = isMobile();
// 先全部清除大卡樣式
$allSlides.find('.video_info').css({
'color' : '',
'position' : '',
'bottom' : '',
'width' : '',
'padding' : '',
'background-image' : '',
'border-radius' : ''
});
$allSlides.find('.video_title h4').css({
'color' : '',
'font-size' : ''
});
if (mobile) {
// 手機:一次一則,全寬,不套大卡樣式
$allSlides.css('width', wrapWidth + 'px');
} else {
// 桌機:第一張 40%,其餘平分
var bigWidth = wrapWidth * 0.4;
var smallTotal = wrapWidth - bigWidth - gap * (visibleCount - 1);
var smallWidth = smallTotal / (visibleCount - 1);
$allSlides.css('width', smallWidth + 'px');
var firstVisibleIdx = total + currentIndex;
var $firstSlide = $allSlides.eq(firstVisibleIdx);
$firstSlide.css('width', bigWidth + 'px');
$firstSlide.find('.video_info').css({
'color' : '#fff',
'position' : 'absolute',
'bottom' : '0',
'width' : '100%',
'padding' : '2em',
'background-image' : 'linear-gradient(180deg, transparent 0, #00000066 40%, #000000a3)',
'border-radius' : '0 0 14px 14px'
});
$firstSlide.find('.video_title h4').css({
'color' : '#fff',
'font-size' : '1.5em'
});
}
}
// 計算 track 的 translateX,讓第一個顯示的 slide 對齊左邊
function getOffset(targetIdx) {
var $allSlides = $track.find('.vw3-slide');
var offset = 0;
var gap = 16;
for (var i = 0; i < targetIdx; i++) {
offset += parseFloat($allSlides.eq(i).css('width')) + gap;
}
return offset;
}
function goTo(newIndex, animate) {
currentIndex = ((newIndex % total) + total) % total;
updateWidths();
var targetIdx = total + currentIndex; // 中間那組的 index
var offset = getOffset(targetIdx);
if (animate === false) {
$track.css('transition', 'none');
$track.css('transform', 'translateX(-' + offset + 'px)');
$track[0].offsetHeight; // force reflow
$track.css('transition', '');
} else {
$track.css('transform', 'translateX(-' + offset + 'px)');
}
}
// 初始化
buildTrack();
goTo(0, false);
$prevBtn.on('click', function() {
goTo(currentIndex - 1, true);
});
$nextBtn.on('click', function() {
goTo(currentIndex + 1, true);
});
$(window).on('resize', function() {
goTo(currentIndex, false);
});
});
</script>