با این کد سبد خرید ووکامرسِت دیگه ساده نیست!
اگر فروشگاه ووکامرسی داری، احتمالاً میدانی که جزئیات کوچک میتوانند تجربهی خرید کاربر را کاملاً تغییر دهند.
یکی از همین جزئیات، لحظهای است که مشتری روی دکمهی «افزودن به سبد خرید» کلیک میکند. معمولاً محصول بدون هیچ اتفاق خاصی به سبد اضافه میشود؛ اما در این آموزش، این مرحله را به یک تجربهی جذاب و متفاوت تبدیل کردیم. 🔥
با این کد، بعد از افزودن محصول به سبد خرید، یک ماشین ارسال فروشگاهی وارد صفحه میشود، محصول را دریافت میکند، آن را داخل بخش بار ماشین قرار میدهد و بعد با افکت دود و حرکت، از صفحه خارج میشود. 🚚💨
✨ این انیمیشن دقیقاً چه کاری انجام میدهد؟
وقتی کاربر روی دکمهی افزودن به سبد خرید کلیک میکند، ابتدا تصویر محصول شناسایی میشود.
بعد از اینکه ووکامرس با موفقیت محصول را از طریق Ajax به سبد خرید اضافه کرد:
- ماشین فروشگاهی از سمت راست وارد صفحه میشود.
- چرخهای ماشین شروع به حرکت میکنند.
- دود از اگزوز ماشین خارج میشود.
- درِ قسمت بار ماشین باز میشود.
- تصویر همان محصول از جایگاه فعلیاش در صفحه پرواز میکند.
- محصول داخل بخش بار ماشین قرار میگیرد.
- یک تیک سبز موفقیت نمایش داده میشود. ✅
- ماشین با افکت نور، حرکت چرخها و دود بیشتر از سمت چپ خارج میشود.
نتیجه؟ یک تجربهی خرید جذابتر که حس حرفهایبودن فروشگاه را به کاربر منتقل میکند.
🛒 نمایش محصولات قبلی داخل ماشین
یکی از بخشهای جذاب این کد این است که فقط محصول جدید را نمایش نمیدهد.
محصولاتی که از قبل داخل سبد خرید کاربر هستند هم به شکل تصویرهای کوچک داخل بخش بار ماشین دیده میشوند. یعنی هر بار که کاربر محصول جدیدی اضافه میکند، حس میکند ماشین واقعاً در حال جمعآوری سفارش اوست.
این جزئیات ساده میتواند تعامل کاربر با فروشگاه را بیشتر کند و ظاهر سایت را از یک فروشگاه معمولی، به یک فروشگاه حرفهای و بهیادماندنی تبدیل کند.
⚡ سازگار با Ajax ووکامرس
نکتهی مهم این است که این انیمیشن قرار نیست در عملکرد اصلی ووکامرس دخالت کند.
یعنی ابتدا ووکامرس خودش محصول را به سبد خرید اضافه میکند و بعد از موفقیت، انیمیشن اجرا میشود.
پس این سیستم:
- قیمت محصول را تغییر نمیدهد.
- موجودی را دستکاری نمیکند.
- سفارش یا سشن سبد خرید را تغییر نمیدهد.
- درخواست Ajax سفارشی و اضافی ایجاد نمیکند.
- روند اصلی خرید کاربر را مختل نمیکند.
در واقع، این فقط یک لایهی بصری جذاب روی فرایند استاندارد ووکامرس است.
📱 واکنشگرا برای موبایل و دسکتاپ
این افکت فقط برای دسکتاپ طراحی نشده است.
ابعاد ماشین، چرخها، بخش بار و محصولات داخل آن برای موبایل هم بهینه شدهاند تا در صفحههای کوچکتر، ظاهر بههمریختهای نداشته باشند.
همچنین برای کاربرانی که در تنظیمات دستگاه خود گزینهی کاهش حرکت را فعال کردهاند، انیمیشن بهصورت خودکار غیرفعال میشود. این یعنی علاوه بر جذابیت، به تجربهی کاربری و دسترسپذیری سایت هم توجه شده است.
🎯 مناسب چه فروشگاههایی است؟
این افکت برای فروشگاههایی که میخواهند حس متفاوتتر و حرفهایتری ایجاد کنند خیلی مناسب است؛ مخصوصاً:
- فروشگاههای پوشاک
- فروشگاههای دیجیتال و گجت
- فروشگاههای هدیه
- فروشگاههای کودک
- فروشگاههای محصولات فانتزی
- فروشگاههایی با برند جوان، خلاق و مدرن
- سایتهایی که میخواهند نرخ تعامل و جذابیت صفحات محصولشان بیشتر شود
کد آماده در رابطه با این آموزش با قابلیت کپی برداری
add_action( 'wp_enqueue_scripts', 'wcmc_delivery_car_assets', 99 );
function wcmc_delivery_car_assets() {
if ( is_admin() || ! class_exists( 'WooCommerce' ) ) {
return;
}
wp_enqueue_script( 'jquery' );
/*
|--------------------------------------------------------------------------
| دریافت تصاویر فعلی محصولات سبد
|--------------------------------------------------------------------------
*/
$wcmc_cart_images = array();
if ( function_exists( 'WC' ) && WC()->cart ) {
foreach ( WC()->cart->get_cart() as $cart_item ) {
$product = isset( $cart_item['data'] ) ? $cart_item['data'] : false;
if ( ! $product || ! is_a( $product, 'WC_Product' ) ) {
continue;
}
$image_id = $product->get_image_id();
/* اگر وارییشن عکس ندارد، عکس محصول اصلی استفاده شود. */
if ( ! $image_id && $product->is_type( 'variation' ) ) {
$parent_product = wc_get_product( $product->get_parent_id() );
if ( $parent_product ) {
$image_id = $parent_product->get_image_id();
}
}
$image_url = $image_id
? wp_get_attachment_image_url( $image_id, 'woocommerce_thumbnail' )
: wc_placeholder_img_src( 'woocommerce_thumbnail' );
if ( $image_url ) {
$wcmc_cart_images[] = esc_url_raw( $image_url );
}
}
}
$wcmc_cart_images = array_slice( $wcmc_cart_images, -4 );
/*
|--------------------------------------------------------------------------
| CSS
|--------------------------------------------------------------------------
*/
$css = <<<'CSS'
#wcmc-layer {
position: fixed;
inset: 0;
z-index: 999999;
overflow: hidden;
pointer-events: none;
}
.wcmc-overlay {
position: absolute;
inset: 0;
opacity: 0;
background:
radial-gradient(circle at 18% 18%, rgba(251,191,36,.18), transparent 28%),
radial-gradient(circle at 82% 78%, rgba(59,130,246,.16), transparent 33%),
rgba(255,255,255,.72);
backdrop-filter: blur(4px);
-webkit-backdrop-filter: blur(4px);
will-change: opacity;
}
/* خودرو */
.wcmc-cart {
position: fixed;
width: 330px;
height: 235px;
opacity: 0;
pointer-events: none;
filter: drop-shadow(0 28px 34px rgba(15,23,42,.28));
will-change: left, top, transform, opacity;
}
.wcmc-cart::before {
content: "";
position: absolute;
z-index: 0;
top: 40px;
left: 25px;
width: 280px;
height: 160px;
border-radius: 50%;
background: radial-gradient(circle, rgba(251,191,36,.30), transparent 68%);
filter: blur(20px);
}
.wcmc-cart::after {
content: "";
position: absolute;
z-index: 0;
bottom: 5px;
left: 38px;
width: 250px;
height: 26px;
border-radius: 50%;
background: rgba(15,23,42,.22);
filter: blur(12px);
}
/* بدنه خودرو */
.wcmc-car-body {
position: absolute;
z-index: 3;
right: 20px;
bottom: 31px;
width: 285px;
height: 95px;
border: 3px solid rgba(255,255,255,.82);
border-radius: 24px 38px 22px 22px;
background:
linear-gradient(120deg, rgba(255,255,255,.28), transparent 25%),
linear-gradient(135deg, #f97316 0%, #ef4444 48%, #be123c 100%);
box-shadow:
inset 0 3px 4px rgba(255,255,255,.38),
inset 0 -12px 18px rgba(127,29,29,.25),
0 18px 24px rgba(190,24,93,.24);
}
.wcmc-car-body::before {
content: "";
position: absolute;
right: -12px;
bottom: 12px;
width: 32px;
height: 34px;
border-radius: 0 12px 12px 0;
background: linear-gradient(180deg, #cbd5e1, #64748b);
box-shadow: inset 0 2px 2px rgba(255,255,255,.45);
}
.wcmc-car-body::after {
content: "";
position: absolute;
top: 13px;
left: 25px;
width: 210px;
height: 3px;
border-radius: 10px;
background: linear-gradient(90deg, transparent, rgba(255,255,255,.92), transparent);
}
/* کابین */
.wcmc-car-cabin {
position: absolute;
z-index: 4;
right: 52px;
bottom: 114px;
width: 128px;
height: 67px;
border: 3px solid rgba(255,255,255,.82);
border-bottom: 0;
border-radius: 58px 38px 0 0;
background:
linear-gradient(145deg, rgba(255,255,255,.55), rgba(125,211,252,.25)),
linear-gradient(135deg, #0ea5e9, #1d4ed8);
box-shadow:
inset 0 2px 4px rgba(255,255,255,.45),
0 6px 12px rgba(30,64,175,.2);
}
.wcmc-car-cabin::before {
content: "";
position: absolute;
top: 5px;
left: 56px;
width: 4px;
height: 56px;
border-radius: 5px;
background: rgba(255,255,255,.7);
transform: rotate(-12deg);
}
.wcmc-car-cabin::after {
content: "";
position: absolute;
right: -16px;
top: 29px;
width: 16px;
height: 12px;
border: 2px solid rgba(255,255,255,.75);
border-radius: 4px 8px 8px 4px;
background: #60a5fa;
}
/* چراغ */
.wcmc-car-light {
position: absolute;
z-index: 8;
right: 7px;
bottom: 73px;
width: 24px;
height: 18px;
border: 2px solid rgba(255,255,255,.95);
border-radius: 11px 5px 5px 11px;
background: #fef08a;
box-shadow: 0 0 15px rgba(250,204,21,.8);
}
.wcmc-cart.is-success .wcmc-car-light {
animation: wcmcHeadlight .32s ease-in-out 3;
}
@keyframes wcmcHeadlight {
50% {
opacity: .35;
box-shadow: 0 0 34px rgba(250,204,21,1);
}
}
/* بخش بار */
.wcmc-basket {
position: absolute;
z-index: 6;
left: 18px;
bottom: 92px;
width: 142px;
height: 106px;
overflow: hidden;
border: 3px solid rgba(255,255,255,.92);
border-radius: 18px 18px 14px 14px;
background:
linear-gradient(135deg, rgba(255,255,255,.25), transparent 38%),
linear-gradient(145deg, #7c3aed, #4338ca);
box-shadow:
inset 0 3px 4px rgba(255,255,255,.35),
inset 0 -13px 18px rgba(30,27,75,.26),
0 12px 20px rgba(67,56,202,.26);
}
.wcmc-basket::before {
content: "";
position: absolute;
inset: 13px 10px auto;
height: 3px;
border-radius: 8px;
background: linear-gradient(90deg, transparent, rgba(255,255,255,.9), transparent);
}
.wcmc-basket::after {
content: "DELIVERY";
position: absolute;
right: 11px;
bottom: 9px;
color: rgba(255,255,255,.85);
font-family: Arial, sans-serif;
font-size: 9px;
font-weight: 800;
letter-spacing: 1px;
}
/* درِ بار */
.wcmc-lid {
position: absolute;
z-index: 9;
left: 11px;
bottom: 183px;
width: 156px;
height: 24px;
border: 3px solid rgba(255,255,255,.92);
border-radius: 14px 14px 7px 7px;
background:
linear-gradient(120deg, rgba(255,255,255,.38), transparent 38%),
linear-gradient(135deg, #a855f7, #4f46e5);
box-shadow:
inset 0 2px 3px rgba(255,255,255,.45),
0 7px 12px rgba(49,46,129,.25);
transform-origin: 14px 100%;
transition: transform .44s cubic-bezier(.2,.9,.2,1);
}
.wcmc-cart.is-open .wcmc-lid {
transform: rotate(-56deg) translate(-10px,-16px);
}
/* چرخها */
.wcmc-wheel {
position: absolute;
z-index: 10;
bottom: 10px;
width: 58px;
height: 58px;
border: 7px solid #1e293b;
border-radius: 50%;
background:
radial-gradient(circle, #f8fafc 0 16%, #64748b 18% 29%, #1e293b 31% 100%);
box-shadow:
inset 0 0 0 5px #0f172a,
0 6px 10px rgba(15,23,42,.35);
}
.wcmc-wheel::after {
content: "";
position: absolute;
inset: 7px;
border: 3px dashed rgba(255,255,255,.45);
border-radius: 50%;
}
.wcmc-wheel-back {
left: 48px;
}
.wcmc-wheel-front {
right: 38px;
}
.wcmc-cart.is-driving .wcmc-wheel {
animation: wcmcWheelSpin .45s linear infinite;
}
@keyframes wcmcWheelSpin {
to {
transform: rotate(360deg);
}
}
/* دود */
.wcmc-smoke {
position: absolute;
z-index: 2;
left: -5px;
bottom: 43px;
width: 65px;
height: 55px;
}
.wcmc-smoke span {
position: absolute;
bottom: 0;
left: 20px;
width: 18px;
height: 18px;
opacity: 0;
border-radius: 50%;
background: rgba(100,116,139,.50);
filter: blur(2px);
}
.wcmc-cart.is-driving .wcmc-smoke span {
animation: wcmcSmoke 1.2s ease-out infinite;
}
.wcmc-cart.is-driving .wcmc-smoke span:nth-child(2) {
animation-delay: .35s;
}
.wcmc-cart.is-driving .wcmc-smoke span:nth-child(3) {
animation-delay: .7s;
}
@keyframes wcmcSmoke {
0% {
opacity: 0;
transform: translate(0,0) scale(.4);
}
30% {
opacity: .6;
}
100% {
opacity: 0;
transform: translate(-58px,-42px) scale(2.1);
}
}
/* محصولات قبلی در بخش بار */
.wcmc-existing-items {
position: absolute;
z-index: 5;
right: 7px;
bottom: 7px;
left: 7px;
display: flex;
align-items: flex-end;
justify-content: center;
min-height: 35px;
direction: ltr;
}
.wcmc-existing-item {
width: 35px;
height: 35px;
overflow: hidden;
margin-left: -9px;
border: 2px solid rgba(255,255,255,.92);
border-radius: 10px;
background: #fff;
box-shadow: 0 5px 10px rgba(30,27,75,.28);
transform: rotate(-5deg);
}
.wcmc-existing-item:first-child {
margin-left: 0;
}
.wcmc-existing-item:nth-child(even) {
transform: rotate(5deg) translateY(-4px);
}
.wcmc-existing-item:last-child {
transform: rotate(3deg) translateY(-7px) scale(1.05);
}
.wcmc-existing-item img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
/*
* ماشین با scaleX(-1) برعکس شده،
* پس عکس محصولات دوباره برمیگردد تا آینهای نشود.
*/
transform: scaleX(-1);
}
/* تصویر محصول جدید */
.wcmc-product-slot {
position: absolute;
z-index: 7;
top: 17px;
left: 50%;
width: 61px;
height: 61px;
overflow: hidden;
opacity: 0;
border: 3px solid rgba(255,255,255,.98);
border-radius: 16px;
background: #fff;
box-shadow:
0 8px 16px rgba(30,27,75,.34),
0 0 0 5px rgba(255,255,255,.18);
transform: translateX(-50%) scale(.4);
}
.wcmc-product-slot img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
transform: scaleX(-1);
}
.wcmc-product-slot::after {
content: "";
position: absolute;
inset: 0;
border-radius: inherit;
background: linear-gradient(
125deg,
rgba(255,255,255,.62) 0%,
transparent 30%,
transparent 63%,
rgba(255,255,255,.22) 100%
);
}
/* تیک موفقیت */
.wcmc-cart-count {
position: absolute;
z-index: 12;
top: 48px;
left: 132px;
display: flex;
align-items: center;
justify-content: center;
width: 31px;
height: 31px;
opacity: 0;
border: 3px solid #fff;
border-radius: 50%;
color: #fff;
font-family: Arial, sans-serif;
font-size: 17px;
font-weight: 900;
background: linear-gradient(135deg, #22c55e, #15803d);
box-shadow: 0 8px 18px rgba(34,197,94,.42);
transform: scaleX(-1) scale(.5);
}
/* تکان موفقیت ماشین؛ scaleX(-1) حفظ شده است */
.wcmc-cart.is-success {
animation: wcmcCarSuccess .72s cubic-bezier(.2,1.45,.45,1);
}
@keyframes wcmcCarSuccess {
0% {
transform: scaleX(-1) scale(1) rotate(0);
}
20% {
transform: scaleX(-1) scale(1.07) rotate(-3deg) translateY(-7px);
}
43% {
transform: scaleX(-1) scale(.97) rotate(3deg) translateY(2px);
}
66% {
transform: scaleX(-1) scale(1.035) rotate(-1deg) translateY(-3px);
}
100% {
transform: scaleX(-1) scale(1) rotate(0);
}
}
/* تصویر محصول هنگام پرواز */
.wcmc-flying-image {
position: fixed;
z-index: 1000002;
display: block;
object-fit: cover;
border: 3px solid #fff;
border-radius: 16px;
background: #fff;
box-shadow: 0 18px 35px rgba(15,23,42,.30);
pointer-events: none;
will-change: left,top,width,height,opacity,transform;
}
/* ذرات */
.wcmc-spark {
position: fixed;
z-index: 1000003;
width: 8px;
height: 8px;
border-radius: 50%;
background: #fbbf24;
box-shadow: 0 0 15px rgba(251,191,36,.9);
pointer-events: none;
}
/* موبایل */
@media (max-width: 640px) {
.wcmc-cart {
width: 255px;
height: 190px;
}
.wcmc-cart::before {
top: 26px;
left: 20px;
width: 220px;
height: 140px;
}
.wcmc-cart::after {
bottom: 0;
left: 28px;
width: 200px;
}
.wcmc-car-body {
right: 13px;
bottom: 25px;
width: 220px;
height: 74px;
border-radius: 19px 28px 17px 17px;
}
.wcmc-car-cabin {
right: 39px;
bottom: 91px;
width: 102px;
height: 51px;
}
.wcmc-car-cabin::before {
left: 45px;
height: 42px;
}
.wcmc-basket {
left: 11px;
bottom: 76px;
width: 112px;
height: 83px;
border-radius: 14px 14px 11px 11px;
}
.wcmc-lid {
left: 7px;
bottom: 147px;
width: 122px;
height: 19px;
border-radius: 11px 11px 5px 5px;
}
.wcmc-cart.is-open .wcmc-lid {
transform: rotate(-51deg) translate(-8px,-11px);
}
.wcmc-wheel {
bottom: 7px;
width: 45px;
height: 45px;
border-width: 5px;
}
.wcmc-wheel-back {
left: 36px;
}
.wcmc-wheel-front {
right: 28px;
}
.wcmc-car-light {
right: 2px;
bottom: 54px;
width: 18px;
height: 13px;
}
.wcmc-product-slot {
top: 13px;
width: 49px;
height: 49px;
border-radius: 13px;
}
.wcmc-existing-item {
width: 28px;
height: 28px;
margin-left: -8px;
border-radius: 8px;
}
.wcmc-cart-count {
top: 37px;
left: 103px;
width: 25px;
height: 25px;
font-size: 14px;
}
}
/* تنظیمات دسترسی کاربران حساس به حرکت */
@media (prefers-reduced-motion: reduce) {
#wcmc-layer {
display: none !important;
}
}
CSS;
wp_register_style( 'wcmc-delivery-car-style', false, array(), '1.1.0' );
wp_enqueue_style( 'wcmc-delivery-car-style' );
wp_add_inline_style( 'wcmc-delivery-car-style', $css );
/*
|--------------------------------------------------------------------------
| انتقال ایمن تصاویر محصولات سبد به JavaScript
|--------------------------------------------------------------------------
*/
wp_add_inline_script(
'jquery',
'window.WCMC_CART_DATA = ' . wp_json_encode(
array(
'images' => array_values( $wcmc_cart_images ),
)
) . ';',
'before'
);
/*
|--------------------------------------------------------------------------
| JavaScript
|--------------------------------------------------------------------------
*/
$js = <<<'JS'
jQuery(function ($) {
'use strict';
try {
if (
window.matchMedia &&
window.matchMedia('(prefers-reduced-motion: reduce)').matches
) {
return;
}
if (!document.getElementById('wcmc-layer')) {
$('body').append(
'<div id="wcmc-layer" aria-hidden="true">' +
'<div class="wcmc-overlay"></div>' +
'</div>'
);
}
const layer = document.getElementById('wcmc-layer');
const overlay = layer.querySelector('.wcmc-overlay');
const queue = [];
const pendingProducts = [];
let busy = false;
let cartImages = (
window.WCMC_CART_DATA &&
Array.isArray(window.WCMC_CART_DATA.images)
)
? window.WCMC_CART_DATA.images.filter(Boolean).slice(-4)
: [];
function wait(ms) {
return new Promise(function(resolve) {
window.setTimeout(resolve, ms);
});
}
function animate(element, frames, options) {
return new Promise(function(resolve) {
if (!element || typeof element.animate !== 'function') {
resolve();
return;
}
try {
const animation = element.animate(frames, options);
animation.onfinish = resolve;
animation.oncancel = resolve;
} catch (error) {
resolve();
}
});
}
function getImageUrl(image) {
if (!image) {
return '';
}
const directUrl =
image.currentSrc ||
image.getAttribute('data-src') ||
image.getAttribute('data-lazy-src') ||
image.getAttribute('data-original') ||
image.getAttribute('data-image') ||
image.getAttribute('src') ||
'';
if (
directUrl &&
directUrl !== 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=='
) {
return directUrl;
}
const srcset =
image.getAttribute('data-srcset') ||
image.getAttribute('srcset') ||
'';
return srcset
? (srcset.split(',')[0].trim().split(' ')[0] || '')
: '';
}
function captureProductImage(button) {
let image = null;
if (button) {
const productBox = button.closest(
'.product, li.product, .type-product, .product-item, article.product, .product-card'
);
if (productBox) {
image = productBox.querySelector(
'.woocommerce-LoopProduct-link img, ' +
'.product-thumbnail img, ' +
'.product-image img, ' +
'.product-thumb img, ' +
'.attachment-woocommerce_thumbnail, img'
);
}
}
if (!image) {
image = document.querySelector(
'.woocommerce-product-gallery__wrapper .woocommerce-product-gallery__image img, ' +
'.woocommerce-product-gallery__image img, ' +
'.woocommerce-product-gallery img, .wp-post-image'
);
}
const fallbackWidth = 70;
const fallbackHeight = 70;
if (!image) {
return {
url: '',
left: (window.innerWidth / 2) - (fallbackWidth / 2),
top: (window.innerHeight / 2) - (fallbackHeight / 2),
width: fallbackWidth,
height: fallbackHeight
};
}
const rect = image.getBoundingClientRect();
return {
url: getImageUrl(image),
left: Math.max(8, rect.left),
top: Math.max(8, rect.top),
width: Math.max(48, rect.width || fallbackWidth),
height: Math.max(48, rect.height || fallbackHeight)
};
}
function createCar(existingImages) {
const car = document.createElement('div');
car.className = 'wcmc-cart';
car.innerHTML =
'<div class="wcmc-smoke"><span></span><span></span><span></span></div>' +
'<div class="wcmc-car-cabin"></div>' +
'<div class="wcmc-car-body"></div>' +
'<div class="wcmc-car-light"></div>' +
'<div class="wcmc-wheel wcmc-wheel-back"></div>' +
'<div class="wcmc-wheel wcmc-wheel-front"></div>' +
'<div class="wcmc-lid"></div>' +
'<div class="wcmc-basket">' +
'<div class="wcmc-existing-items"></div>' +
'<div class="wcmc-product-slot"></div>' +
'</div>' +
'<div class="wcmc-cart-count">✓</div>';
const existingItemsBox = car.querySelector('.wcmc-existing-items');
const images = Array.isArray(existingImages)
? existingImages.slice(-4)
: [];
images.forEach(function(imageUrl) {
if (!imageUrl) {
return;
}
const item = document.createElement('div');
const image = document.createElement('img');
item.className = 'wcmc-existing-item';
image.alt = '';
image.src = imageUrl;
item.appendChild(image);
existingItemsBox.appendChild(item);
});
layer.appendChild(car);
return car;
}
function createFlyingImage(productData) {
const image = document.createElement('img');
image.className = 'wcmc-flying-image';
image.alt = '';
image.src = productData && productData.url ? productData.url : '';
image.style.left = Math.max(8, productData.left) + 'px';
image.style.top = Math.max(8, productData.top) + 'px';
image.style.width = productData.width + 'px';
image.style.height = productData.height + 'px';
layer.appendChild(image);
return image;
}
function createSpark(x, y) {
const spark = document.createElement('span');
spark.className = 'wcmc-spark';
spark.style.left = x + 'px';
spark.style.top = y + 'px';
layer.appendChild(spark);
const xMove = (Math.random() - 0.5) * 120;
const yMove = (Math.random() - 0.5) * 95;
animate(
spark,
[
{
transform: 'translate(0,0) scale(1)',
opacity: 1
},
{
transform: 'translate(' + xMove + 'px,' + yMove + 'px) scale(.15)',
opacity: 0
}
],
{
duration: 480,
easing: 'cubic-bezier(.2,.8,.2,1)',
fill: 'forwards'
}
).then(function() {
spark.remove();
});
}
async function playAnimation(productData) {
const mobile = window.innerWidth <= 640;
const carWidth = mobile ? 255 : 330;
const carHeight = mobile ? 190 : 235;
const centerLeft = Math.round((window.innerWidth - carWidth) / 2);
const centerTop = Math.max(
35,
Math.round((window.innerHeight - carHeight) / 2)
);
await animate(
overlay,
[
{ opacity: 0 },
{ opacity: 1 }
],
{
duration: 280,
easing: 'ease-out',
fill: 'forwards'
}
);
const car = createCar(cartImages);
const productSlot = car.querySelector('.wcmc-product-slot');
const counter = car.querySelector('.wcmc-cart-count');
car.style.left = (window.innerWidth + 50) + 'px';
car.style.top = centerTop + 'px';
car.style.opacity = '1';
car.classList.add('is-driving');
/*
* ورود از سمت راست
* scaleX(-1) یعنی دماغه خودرو به سمت چپ باشد.
*/
await animate(
car,
[
{
left: (window.innerWidth + 50) + 'px',
transform: 'scaleX(-1) scale(.72) rotate(7deg)',
opacity: 0
},
{
left: centerLeft + 'px',
transform: 'scaleX(-1) scale(1) rotate(0deg)',
opacity: 1
}
],
{
duration: 850,
easing: 'cubic-bezier(.16,1,.3,1)',
fill: 'forwards'
}
);
car.style.left = centerLeft + 'px';
car.classList.remove('is-driving');
car.classList.add('is-open');
await wait(280);
const slotRect = productSlot.getBoundingClientRect();
const targetLeft = slotRect.left;
const targetTop = slotRect.top;
const flyingImage = createFlyingImage(productData);
await animate(
flyingImage,
[
{
left: flyingImage.style.left,
top: flyingImage.style.top,
width: flyingImage.style.width,
height: flyingImage.style.height,
transform: 'rotate(0deg) scale(1)',
opacity: 1
},
{
left: targetLeft + 'px',
top: targetTop + 'px',
width: slotRect.width + 'px',
height: slotRect.height + 'px',
transform: 'rotate(540deg) scale(1)',
opacity: 1
}
],
{
duration: 760,
easing: 'cubic-bezier(.18,.78,.22,1)',
fill: 'forwards'
}
);
const productInCar = document.createElement('img');
productInCar.alt = '';
productInCar.src = productData && productData.url
? productData.url
: '';
productSlot.appendChild(productInCar);
flyingImage.remove();
if (productData && productData.url) {
cartImages.push(productData.url);
cartImages = cartImages.filter(Boolean).slice(-4);
}
await animate(
productSlot,
[
{
opacity: 0,
transform: 'translateX(-50%) scale(.4)'
},
{
opacity: 1,
transform: 'translateX(-50%) scale(1.16)'
},
{
opacity: 1,
transform: 'translateX(-50%) scale(1)'
}
],
{
duration: 380,
easing: 'cubic-bezier(.2,.9,.2,1)',
fill: 'forwards'
}
);
/*
* تیک نیز scaleX(-1) دارد تا برعکس نشود.
*/
await animate(
counter,
[
{
opacity: 0,
transform: 'scaleX(-1) scale(.5)'
},
{
opacity: 1,
transform: 'scaleX(-1) scale(1.18)'
},
{
opacity: 1,
transform: 'scaleX(-1) scale(1)'
}
],
{
duration: 350,
easing: 'cubic-bezier(.2,.9,.2,1)',
fill: 'forwards'
}
);
for (let i = 0; i < 12; i++) {
createSpark(
targetLeft + (slotRect.width / 2) + ((Math.random() - 0.5) * 32),
targetTop + (slotRect.height / 2) + ((Math.random() - 0.5) * 28)
);
}
await wait(170);
car.classList.add('is-success');
await wait(720);
car.classList.remove('is-success');
car.classList.remove('is-open');
car.classList.add('is-driving');
/*
* خروج به سمت چپ،
* با حفظ جهت چپبودن دماغه ماشین.
*/
await animate(
car,
[
{
left: centerLeft + 'px',
transform: 'scaleX(-1) scale(1) rotate(0deg)',
opacity: 1
},
{
left: (mobile ? -280 : -390) + 'px',
transform: 'scaleX(-1) scale(.55) rotate(-5deg)',
opacity: 0
}
],
{
duration: 920,
easing: 'cubic-bezier(.55,0,.95,.45)',
fill: 'forwards'
}
);
await animate(
overlay,
[
{ opacity: 1 },
{ opacity: 0 }
],
{
duration: 340,
easing: 'ease-out',
fill: 'forwards'
}
);
car.remove();
}
async function processQueue() {
if (busy) {
return;
}
busy = true;
while (queue.length > 0) {
await playAnimation(queue.shift());
await wait(120);
}
busy = false;
}
/*
* پیش از ارسال Ajax:
* تصویر و موقعیت محصول ثبت میشود.
*/
$(document.body).on(
'adding_to_cart.wcmc',
function(event, $button) {
const button = $button && $button[0]
? $button[0]
: null;
pendingProducts.push(captureProductImage(button));
}
);
/*
* پس از موفقیت Ajax ووکامرس:
* فقط انیمیشن اجرا میشود و هیچ دخالتی در سبد ندارد.
*/
$(document.body).on(
'added_to_cart.wcmc',
function(event, fragments, cartHash, $button) {
const button = $button && $button[0]
? $button[0]
: null;
const productData = pendingProducts.length
? pendingProducts.shift()
: captureProductImage(button);
queue.push(productData);
processQueue();
}
);
} catch (error) {
console.warn('Delivery car animation skipped safely.', error);
}
});
JS;
wp_add_inline_script( 'jquery', $js, 'after' );
}
راهنمای گامبهگام: چگونه این کد را به سایت وردپرسی خود اضافه کنیم؟
برای اضافه کردن این کد به وردپرس، چندین روش وجود دارد. در ادامه ایمنترین و استانداردترین روشها را با هم بررسی میکنیم:
روش اول: ویرایش از طریق مدیریت فایل هاست (روش حرفهای)
گاهی اوقات ممکن است دسترسی به ویرایشگر وردپرس (Theme File Editor) به دلایل امنیتی توسط توسعهدهنده قالب بسته شده باشد، یا شاید ترجیح میدهید به جای محیط وردپرس، مستقیماً با فایلهای سرور کار کنید. در این صورت، استفاده از File Manager هاست بهترین راهکار است.
گامهای اجرای کار در هاست (cPanel یا DirectAdmin):
- ورود به هاست: ابتدا وارد پنل مدیریت هاست خود (cPanel یا DirectAdmin) شوید.
- باز کردن فایل منیجر: روی گزینه File Manager کلیک کنید.
- مسیریابی: به پوشه ریشه سایت خود (معمولاً
public_html) بروید. سپس مسیر زیر را دنبال کنید:wp-content>themes>your-theme-name(نکته: بهجایyour-theme-nameنام پوسته فعال سایت خود را انتخاب کنید. اگر از Child Theme استفاده میکنید، حتماً وارد پوشه فرزند شوید). - ویرایش فایل: فایل
functions.phpرا پیدا کنید. روی آن راستکلیک کرده و گزینه Edit را انتخاب کنید. - جایگذاری کد: کد مورد نظر خود را به انتهای فایل اضافه کنید.
- ذخیره: دکمه Save Changes را در بالای صفحه ویرایشگر بزنید.
روش دوم : اضافه کردن کد به فایل functions.php (روش سریع)
فایل functions.php قلب تپنده قالب شماست و کدهای شخصیسازی در آن قرار میگیرند.
- وارد پیشخوان وردپرس شوید: از منوی سمت راست به مسیر نمایش > ویرایشگر پرونده پوسته (Theme File Editor) بروید.
- فایل مناسب را انتخاب کنید: از ستون سمت چپ (بخش پروندههای پوسته)، فایل توابع پوسته (functions.php) را پیدا کرده و روی آن کلیک کنید.
- قرار دادن کد: به انتهای فایل بروید، یک خط جدید ایجاد کنید و کدهای بالا را دقیقاً در انتهای فایل پیست (Paste) کنید.
- ذخیره تغییرات: روی دکمه بهروزرسانی پرونده کلیک کنید.
⚠️ یک هشدار امنیتی بسیار مهم:
پیش از ویرایش فایلهای قالب، حتماً یک نسخه پشتیبان (Backup) از فایل تهیه کنید. همچنین اگر قالب خود را آپدیت کنید، تغییراتی که در فایل
functions.phpقالب اصلی اعمال کردهاید پاک خواهند شد. برای جلوگیری از این مشکل، پیشنهاد میکنیم از پوسته فرزند (Child Theme) استفاده کنید یا به سراغ روش دوم بروید.
روش سوم: استفاده از افزونههای مدیریت کدهای سفارشی (روش ایمن و بدون خطر)
اگر نمیخواهید فایلهای قالب را به صورت دستی ویرایش کنید و نگران به هم ریختن سایت یا پاک شدن کدها در آپدیتهای بعدی قالب هستید، این روش بهترین گزینه است.
- افزونه رایگان WPcode (Insert Headers and Footers) یا Code Snippets را از مخزن وردپرس نصب و فعال کنید.
- از منوی جدیدی که در پیشخوان اضافه شده، روی Add Snippet (افزودن کد جدید) کلیک کنید.
- یک عنوان دلخواه برای آن انتخاب کنید (مثلاً: نوار اعلان بالای هدر).
- نوع کد (Code Type) را روی حالت PHP Snippet قرار دهید.
- کدهای بالا را در باکس مربوطه کپی کنید.
- وضعیت را روی Active (فعال) قرار داده و دکمه Save را بزنید.
درباره میلاد علیزاده وکیلی
میلاد علیزاده هستم مدرس دوره های آموزش برنامه نویسی در شهر تبریز که فعالیت خودم رو از سال 95 در زمینه ی طراحی و میزبانی وبسایت شروع کردم. هدف من از راه اندازی این وبسایت ارائه ی آموزشی متفاوت همچنین اشتغال محور و پروژه محور هستش که هنرجو بعد از آموزش بتواند با دانش و تجربه ای کافی مستقیما وارد بازار کار شده و در زمینه ی برنامه نویسی کسب درامد کند.
نوشتههای بیشتر از میلاد علیزاده وکیلی

دیدگاهتان را بنویسید