poster.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ポスター費用お見積もり | めいほう出版</title>
<link rel="preconnect" href="<https://fonts.googleapis.com>">
<link rel="preconnect" href="<https://fonts.gstatic.com>" crossorigin>
<link href="<https://fonts.googleapis.com/css2?family=Jost:wght@400;500;600;700&family=Zen+Kaku+Gothic+New:wght@400;500;700&display=swap>" rel="stylesheet">
<style>
/* --- ベース設定 --- */
:root {
--font-jp: 'Zen Kaku Gothic New', "游ゴシック Medium", "游ゴシック体", "Yu Gothic Medium", YuGothic, "ヒラギノ角ゴ ProN", "Hiragino Kaku Gothic ProN", "メイリオ", Meiryo, "MS Pゴシック", "MS PGothic", sans-serif;
--font-en: 'Jost', 'Zen Kaku Gothic New', "游ゴシック Medium", "游ゴシック体", "Yu Gothic Medium", YuGothic, "ヒラギノ角ゴ ProN", "Hiragino Kaku Gothic ProN", "メイリオ", Meiryo, "MS Pゴシック", "MS PGothic", sans-serif;
/* カラーパレット */
--bg-color: #faf9f7;
--text-main: #33322e;
--text-light: #7a7770;
--border-color: #e2dfd8;
--accent-color: #0092b4;
--active-bg: #f2f9fb;
--white: #ffffff;
}
body {
font-family: var(--font-en);
padding: 60px 20px;
background-color: var(--bg-color);
color: var(--text-main);
margin: 0;
letter-spacing: 0.04em;
line-height: 1.6;
}
/* メインコンテナ */
.calculator {
max-width: 640px;
margin: auto;
background: var(--white);
padding: 48px 40px;
border-radius: 4px;
border: 1px solid var(--border-color);
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.02);
}
h2 {
text-align: center;
font-size: 1.5rem;
font-weight: 700;
margin-top: 0;
margin-bottom: 40px;
letter-spacing: 0.1em;
color: var(--text-main);
}
.section-title {
font-size: 1rem;
font-weight: 700;
color: var(--text-main);
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px solid var(--border-color);
}
.btn-group {
display: flex;
gap: 8px;
margin-bottom: 32px;
flex-wrap: wrap;
}
/* 選択ボタン */
.select-btn {
flex: 1;
min-width: calc(25% - 8px);
padding: 12px;
border: 1px solid var(--border-color);
background: var(--white);
cursor: pointer;
border-radius: 2px;
font-size: 0.95rem;
font-family: var(--font-en);
font-weight: 500;
color: var(--text-light);
transition: all 0.3s ease;
text-align: center;
}
.select-btn:hover {
background: var(--active-bg);
color: var(--text-main);
border-color: #b3dfea;
}
.select-btn.active {
background: var(--accent-color);
color: var(--white);
border-color: var(--accent-color);
}
/* プルダウンのスタイル */
.select-dropdown {
width: 100%;
padding: 14px 16px;
border: 1px solid var(--border-color);
border-radius: 2px;
font-size: 0.95rem;
font-family: var(--font-en);
font-weight: 500;
color: var(--text-main);
background-color: var(--white);
cursor: pointer;
transition: all 0.3s;
margin-bottom: 32px;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='<http://www.w3.org/2000/svg>' fill='none' viewBox='0 0 24 24' stroke='%237a7770'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M19 9l-7 7-7-7'%3E%3C/path%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 16px center;
background-size: 16px;
}
.select-dropdown:focus {
outline: none;
border-color: var(--accent-color);
box-shadow: 0 0 0 2px rgba(0, 146, 180, 0.1);
}
/* 部数コントロール */
.qty-control {
display: inline-flex;
align-items: center;
border: 1px solid var(--border-color);
border-radius: 2px;
margin-bottom: 32px;
background: var(--white);
transition: border-color 0.3s;
}
.qty-control:focus-within {
border-color: var(--accent-color);
box-shadow: 0 0 0 2px rgba(0, 146, 180, 0.1);
}
.qty-btn {
width: 48px;
height: 48px;
border: none;
background: transparent;
cursor: pointer;
font-size: 18px;
color: var(--text-light);
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
}
.qty-btn:hover {
background: var(--active-bg);
color: var(--accent-color);
}
#qty-val {
font-size: 1.1rem;
font-weight: 600;
width: 100px;
text-align: center;
color: var(--text-main);
}
/* 無効化(グレーアウト)時 */
.print-options-wrapper {
transition: opacity 0.4s ease;
}
.disabled-section {
opacity: 0.3;
pointer-events: none;
user-select: none;
}
/* 金額表示エリア */
.price-display-wrapper {
margin-top: 40px;
padding-top: 32px;
border-top: 1px solid var(--border-color);
text-align: center;
}
.price-label {
font-size: 0.9rem;
font-weight: 700;
color: var(--text-light);
margin-bottom: 8px;
letter-spacing: 0.05em;
}
.price-display {
font-family: var(--font-en);
font-size: 3.2rem;
font-weight: 600;
color: var(--text-main);
margin: 0;
line-height: 1;
letter-spacing: 0.02em;
display: flex;
align-items: baseline;
justify-content: center;
}
.price-suffix {
font-family: var(--font-jp);
font-size: 1.6rem;
font-weight: 500;
color: var(--text-main);
margin-left: 4px;
}
/* 割引オプション(チェックボックス)のスタイル */
.discount-option {
display: inline-flex;
align-items: center;
justify-content: center;
background-color: var(--active-bg);
padding: 12px 20px;
margin-top: 16px;
border-radius: 4px;
border: 1px solid #b3dfea;
cursor: pointer;
font-family: var(--font-jp);
font-size: 0.95rem;
color: var(--text-main);
transition: all 0.2s ease;
}
.discount-option:hover {
background-color: #e5f4f8;
}
.discount-option input[type="checkbox"] {
margin-right: 10px;
width: 18px;
height: 18px;
accent-color: var(--accent-color);
cursor: pointer;
}
.discount-option strong {
color: var(--accent-color);
font-size: 1.1rem;
font-family: var(--font-en);
margin-left: 6px;
letter-spacing: 0.02em;
}
/* 注記のスタイル */
.price-note {
font-family: var(--font-jp);
font-size: 0.75rem;
color: var(--text-light);
margin-top: 24px;
line-height: 1.5;
text-align: left;
background-color: #f9f9f9;
padding: 12px;
border-radius: 4px;
}
/* スマホ対応 */
@media (max-width: 480px) {
.calculator { padding: 32px 20px; }
.select-btn { min-width: calc(50% - 4px); }
.price-display { font-size: 2.8rem; }
.price-suffix { font-size: 1.4rem; }
.discount-option { font-size: 0.85rem; padding: 10px 12px; width: 100%; box-sizing: border-box; }
}
</style>
</head>
<body>
<div class="calculator">
<h2>お見積もりシミュレーション</h2>
<div class="section-title">ご依頼内容</div>
<select id="service-type" class="select-dropdown" onchange="updateServiceType()">
<option value="design_print">デザイン制作 + 印刷</option>
<option value="design_only">デザイン制作のみ</option>
</select>
<div class="section-title">ポスターサイズ</div>
<div class="btn-group" id="size-selector">
<button class="select-btn active" onclick="updateState('size', 'A3')">A3</button>
<button class="select-btn" onclick="updateState('size', 'A2')">A2</button>
<button class="select-btn" onclick="updateState('size', 'A1')">A1</button>
<button class="select-btn" onclick="updateState('size', 'A0')">A0</button>
<button class="select-btn" onclick="updateState('size', 'B3')">B3</button>
<button class="select-btn" onclick="updateState('size', 'B2')">B2</button>
<button class="select-btn" onclick="updateState('size', 'B1')">B1</button>
<button class="select-btn" onclick="updateState('size', 'B0')">B0</button>
</div>
<div id="print-options-wrapper" class="print-options-wrapper">
<div class="section-title">用紙(135kg)</div>
<div class="btn-group" id="paper-selector">
<button class="select-btn active" onclick="updateState('paper', 'coat')">コート紙</button>
<button class="select-btn" onclick="updateState('paper', 'matte')">マットコート紙</button>
<button class="select-btn" onclick="updateState('paper', 'fine')">上質紙</button>
</div>
<div class="section-title">印刷部数</div>
<div class="qty-control">
<button class="qty-btn" onclick="stepQuantity(-1)">−</button>
<span id="qty-val">10部</span>
<button class="qty-btn" onclick="stepQuantity(1)">+</button>
</div>
</div>
<div class="section-title">データ納品オプション</div>
<select id="data-delivery" class="select-dropdown" onchange="updateDataOption()">
<option value="none">希望しない(印刷物のみ納品)</option>
<option value="pdf">印刷用PDFデータをお渡し (+5,000円)</option>
<option value="ai">Illustratorデータ(アウトライン済) (+15,000円)</option>
<option value="ai_edit">Illustratorデータ(編集可能) (+30,000円)</option>
</select>
<div class="price-display-wrapper">
<div class="price-label">概算費用(税込)</div>
<div class="price-display">
¥<span id="final-price" style="font-size: inherit; color: inherit; font-family: inherit; margin-left: 0; font-weight: inherit;">0</span><span class="price-suffix">〜</span>
</div>
<label class="discount-option">
<input type="checkbox" id="local-discount" onchange="toggleLocalDiscount()">
地域おこし・地方活性化の取り組みなら<strong>-20%</strong>
</label>
<div class="price-note">
※その他、用紙・加工・特急・工数などの要因により変動する場合がございます。詳細はお問い合わせ後に改めてご案内いたします。
</div>
</div>
</div>
<script>
const CONFIG = {
PAPER_SURCHARGE: 1650,
DATA_FEES: {
none: 0,
pdf: 5000,
ai: 15000,
ai_edit: 30000
}
};
const DESIGN_PRICE = {
A3: 25000, A2: 30000, A1: 40000, A0: 50000,
B3: 25000, B2: 35000, B1: 45000, B0: 60000
};
const PRICE_DATA = {
A3: { 10: 31770, 50: 33180, 90: 34590, 100: 36290, 500: 38010, 1000: 40180, 3000: 50740 },
A2: { 10: 42540, 50: 43340, 100: 44320, 300: 45590, 3000: 69950 },
A1: { 10: 52410, 50: 53820, 100: 55590, 300: 57770, 3000: 100160 },
A0: { 10: 159020, 100: 176580, 1000: 229530 },
B3: { 10: 39460, 50: 40060, 100: 40800, 300: 41790, 3000: 60210 },
B2: { 10: 46840, 50: 47890, 100: 49220, 300: 50890, 3000: 82700 },
B1: { 10: 63470, 50: 65560, 100: 68190, 300: 71430, 3000: 128420 },
B0: { 10: 220940, 100: 247200, 1000: 326290 }
};
let state = {
serviceType: 'design_print',
size: 'A3',
paper: 'coat',
qty: 10,
dataDelivery: 'none',
isLocalDiscount: false // 割引チェック状態
};
let currentDisplayPrice = 0;
let animationId = null;
function updateServiceType() {
state.serviceType = document.getElementById('service-type').value;
const printOptionsWrapper = document.getElementById('print-options-wrapper');
const dataDeliverySelect = document.getElementById('data-delivery');
if (state.serviceType === 'design_only') {
printOptionsWrapper.classList.add('disabled-section');
if (state.dataDelivery === 'none') {
dataDeliverySelect.value = 'pdf';
state.dataDelivery = 'pdf';
}
dataDeliverySelect.options[0].disabled = true;
} else {
printOptionsWrapper.classList.remove('disabled-section');
dataDeliverySelect.options[0].disabled = false;
}
calculate();
}
function updateState(key, val) {
state[key] = val;
if (key === 'size') {
const maxQty = (state.size === 'A0' || state.size === 'B0') ? 1000 : 3000;
if (state.qty > maxQty) {
state.qty = maxQty;
updateQtyUI();
}
}
updateButtonActiveStates();
calculate();
}
function updateButtonActiveStates() {
const sizeBtns = document.getElementById('size-selector').querySelectorAll('.select-btn');
sizeBtns.forEach(b => {
b.classList.toggle('active', b.innerText === state.size);
});
const paperMap = { 'coat': 'コート紙', 'matte': 'マットコート紙', 'fine': '上質紙' };
const paperBtns = document.getElementById('paper-selector').querySelectorAll('.select-btn');
paperBtns.forEach(b => {
b.classList.toggle('active', b.innerText === paperMap[state.paper]);
});
}
function updateDataOption() {
state.dataDelivery = document.getElementById('data-delivery').value;
calculate();
}
// 割引チェックボックスの切り替え
function toggleLocalDiscount() {
state.isLocalDiscount = document.getElementById('local-discount').checked;
calculate();
}
function stepQuantity(direction) {
let step = 10;
if (state.qty >= 100 && direction > 0) step = 100;
if (state.qty > 100 && direction < 0) step = 100;
let next = state.qty + (direction * step);
if (next < 10) next = 10;
const max = (state.size === 'A0' || state.size === 'B0') ? 1000 : 3000;
if (next > max) next = max;
state.qty = next;
updateQtyUI();
}
function updateQtyUI() {
document.getElementById('qty-val').innerHTML = `${state.qty.toLocaleString()}部`;
calculate();
}
function interpolate(table, qty) {
const points = Object.keys(table).map(Number).sort((a, b) => a - b);
if (table[qty]) return table[qty];
if (qty <= points[0]) return table[points[0]];
if (qty >= points[points.length - 1]) return table[points[points.length - 1]];
let lower = points[0], upper = points[points.length - 1];
for (let i = 0; i < points.length - 1; i++) {
if (qty > points[i] && qty < points[i+1]) {
lower = points[i];
upper = points[i+1];
break;
}
}
const ratio = (qty - lower) / (upper - lower);
return table[lower] + (table[upper] - table[lower]) * ratio;
}
function calculate() {
let base = 0;
if (state.serviceType === 'design_only') {
base = DESIGN_PRICE[state.size];
} else {
const table = PRICE_DATA[state.size];
base = interpolate(table, state.qty);
if (state.paper !== 'coat') {
base += CONFIG.PAPER_SURCHARGE;
}
}
base += CONFIG.DATA_FEES[state.dataDelivery];
// ▼ 地域活性化割引の適用(20%オフ)
if (state.isLocalDiscount) {
base = base * 0.8;
}
// 10円単位で切り上げ
const final = Math.ceil(base / 10) * 10;
animatePrice(final, 400);
}
function animatePrice(targetPrice, duration) {
const el = document.getElementById('final-price');
const startPrice = currentDisplayPrice;
const startTime = performance.now();
if (animationId) cancelAnimationFrame(animationId);
function update(currentTime) {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
const ease = progress === 1 ? 1 : 1 - Math.pow(2, -10 * progress);
const current = Math.floor(startPrice + (targetPrice - startPrice) * ease);
el.innerText = current.toLocaleString();
currentDisplayPrice = current;
if (progress < 1) {
animationId = requestAnimationFrame(update);
} else {
el.innerText = targetPrice.toLocaleString();
}
}
animationId = requestAnimationFrame(update);
}
// 初期化実行
updateServiceType();
updateButtonActiveStates();
updateQtyUI();
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ポスター費用お見積もり | めいほう出版</title>
<link rel="preconnect" href="<https://fonts.googleapis.com>">
<link rel="preconnect" href="<https://fonts.gstatic.com>" crossorigin>
<link href="<https://fonts.googleapis.com/css2?family=Jost:wght@400;500;600;700&family=Zen+Kaku+Gothic+New:wght@400;500;700&display=swap>" rel="stylesheet">
<style>
/* --- ベース設定 --- */
:root {
--font-jp: 'Zen Kaku Gothic New', "游ゴシック Medium", "游ゴシック体", "Yu Gothic Medium", YuGothic, "ヒラギノ角ゴ ProN", "Hiragino Kaku Gothic ProN", "メイリオ", Meiryo, "MS Pゴシック", "MS PGothic", sans-serif;
--font-en: 'Jost', 'Zen Kaku Gothic New', "游ゴシック Medium", "游ゴシック体", "Yu Gothic Medium", YuGothic, "ヒラギノ角ゴ ProN", "Hiragino Kaku Gothic ProN", "メイリオ", Meiryo, "MS Pゴシック", "MS PGothic", sans-serif;
/* カラーパレット */
--bg-color: #faf9f7;
--text-main: #33322e;
--text-light: #7a7770;
--border-color: #e2dfd8;
--accent-color: #0092b4;
--active-bg: #f2f9fb;
--white: #ffffff;
}
body {
font-family: var(--font-en);
padding: 60px 20px;
background-color: var(--bg-color);
color: var(--text-main);
margin: 0;
letter-spacing: 0.04em;
line-height: 1.6;
}
/* メインコンテナ */
.calculator {
max-width: 640px;
margin: auto;
background: var(--white);
padding: 48px 40px;
border-radius: 4px;
border: 1px solid var(--border-color);
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.02);
}
h2 {
text-align: center;
font-size: 1.5rem;
font-weight: 700;
margin-top: 0;
margin-bottom: 40px;
letter-spacing: 0.1em;
color: var(--text-main);
}
.section-title {
font-size: 1rem;
font-weight: 700;
color: var(--text-main);
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px solid var(--border-color);
}
.btn-group {
display: flex;
gap: 8px;
margin-bottom: 32px;
flex-wrap: wrap;
}
/* 選択ボタン */
.select-btn {
flex: 1;
min-width: calc(25% - 8px);
padding: 12px;
border: 1px solid var(--border-color);
background: var(--white);
cursor: pointer;
border-radius: 2px;
font-size: 0.95rem;
font-family: var(--font-en);
font-weight: 500;
color: var(--text-light);
transition: all 0.3s ease;
text-align: center;
}
.select-btn:hover {
background: var(--active-bg);
color: var(--text-main);
border-color: #b3dfea;
}
.select-btn.active {
background: var(--accent-color);
color: var(--white);
border-color: var(--accent-color);
}
/* プルダウンのスタイル */
.select-dropdown {
width: 100%;
padding: 14px 16px;
border: 1px solid var(--border-color);
border-radius: 2px;
font-size: 0.95rem;
font-family: var(--font-en);
font-weight: 500;
color: var(--text-main);
background-color: var(--white);
cursor: pointer;
transition: all 0.3s;
margin-bottom: 32px;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='<http://www.w3.org/2000/svg>' fill='none' viewBox='0 0 24 24' stroke='%237a7770'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M19 9l-7 7-7-7'%3E%3C/path%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 16px center;
background-size: 16px;
}
.select-dropdown:focus {
outline: none;
border-color: var(--accent-color);
box-shadow: 0 0 0 2px rgba(0, 146, 180, 0.1);
}
/* 部数コントロール */
.qty-control {
display: inline-flex;
align-items: center;
border: 1px solid var(--border-color);
border-radius: 2px;
margin-bottom: 32px;
background: var(--white);
transition: border-color 0.3s;
}
.qty-control:focus-within {
border-color: var(--accent-color);
box-shadow: 0 0 0 2px rgba(0, 146, 180, 0.1);
}
.qty-btn {
width: 48px;
height: 48px;
border: none;
background: transparent;
cursor: pointer;
font-size: 18px;
color: var(--text-light);
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
}
.qty-btn:hover {
background: var(--active-bg);
color: var(--accent-color);
}
#qty-val {
font-size: 1.1rem;
font-weight: 600;
width: 100px;
text-align: center;
color: var(--text-main);
}
/* 無効化(グレーアウト)時 */
.print-options-wrapper {
transition: opacity 0.4s ease;
}
.disabled-section {
opacity: 0.3;
pointer-events: none;
user-select: none;
}
/* 金額表示エリア */
.price-display-wrapper {
margin-top: 40px;
padding-top: 32px;
border-top: 1px solid var(--border-color);
text-align: center;
}
.price-label {
font-size: 0.9rem;
font-weight: 700;
color: var(--text-light);
margin-bottom: 8px;
letter-spacing: 0.05em;
}
.price-display {
font-family: var(--font-en);
font-size: 3.2rem;
font-weight: 600;
color: var(--text-main);
margin: 0;
line-height: 1;
letter-spacing: 0.02em;
display: flex;
align-items: baseline;
justify-content: center;
}
.price-suffix {
font-family: var(--font-jp);
font-size: 1.6rem;
font-weight: 500;
color: var(--text-main);
margin-left: 4px;
}
/* 割引オプション(チェックボックス)のスタイル */
.discount-option {
display: inline-flex;
align-items: center;
justify-content: center;
background-color: var(--active-bg);
padding: 12px 20px;
margin-top: 16px;
border-radius: 4px;
border: 1px solid #b3dfea;
cursor: pointer;
font-family: var(--font-jp);
font-size: 0.95rem;
color: var(--text-main);
transition: all 0.2s ease;
}
.discount-option:hover {
background-color: #e5f4f8;
}
.discount-option input[type="checkbox"] {
margin-right: 10px;
width: 18px;
height: 18px;
accent-color: var(--accent-color);
cursor: pointer;
}
.discount-option strong {
color: var(--accent-color);
font-size: 1.1rem;
font-family: var(--font-en);
margin-left: 6px;
letter-spacing: 0.02em;
}
/* 注記のスタイル */
.price-note {
font-family: var(--font-jp);
font-size: 0.75rem;
color: var(--text-light);
margin-top: 24px;
line-height: 1.5;
text-align: left;
background-color: #f9f9f9;
padding: 12px;
border-radius: 4px;
}
/* スマホ対応 */
@media (max-width: 480px) {
.calculator { padding: 32px 20px; }
.select-btn { min-width: calc(50% - 4px); }
.price-display { font-size: 2.8rem; }
.price-suffix { font-size: 1.4rem; }
.discount-option { font-size: 0.85rem; padding: 10px 12px; width: 100%; box-sizing: border-box; }
}
</style>
</head>
<body>
<div class="calculator">
<h2>お見積もりシミュレーション</h2>
<div class="section-title">ご依頼内容</div>
<select id="service-type" class="select-dropdown" onchange="updateServiceType()">
<option value="design_print">デザイン制作 + 印刷</option>
<option value="design_only">デザイン制作のみ</option>
</select>
<div class="section-title">ポスターサイズ</div>
<div class="btn-group" id="size-selector">
<button class="select-btn active" onclick="updateState('size', 'A3')">A3</button>
<button class="select-btn" onclick="updateState('size', 'A2')">A2</button>
<button class="select-btn" onclick="updateState('size', 'A1')">A1</button>
<button class="select-btn" onclick="updateState('size', 'A0')">A0</button>
<button class="select-btn" onclick="updateState('size', 'B3')">B3</button>
<button class="select-btn" onclick="updateState('size', 'B2')">B2</button>
<button class="select-btn" onclick="updateState('size', 'B1')">B1</button>
<button class="select-btn" onclick="updateState('size', 'B0')">B0</button>
</div>
<div id="print-options-wrapper" class="print-options-wrapper">
<div class="section-title">用紙(135kg)</div>
<div class="btn-group" id="paper-selector">
<button class="select-btn active" onclick="updateState('paper', 'coat')">コート紙</button>
<button class="select-btn" onclick="updateState('paper', 'matte')">マットコート紙</button>
<button class="select-btn" onclick="updateState('paper', 'fine')">上質紙</button>
</div>
<div class="section-title">印刷部数</div>
<div class="qty-control">
<button class="qty-btn" onclick="stepQuantity(-1)">−</button>
<span id="qty-val">10部</span>
<button class="qty-btn" onclick="stepQuantity(1)">+</button>
</div>
</div>
<div class="section-title">データ納品オプション</div>
<select id="data-delivery" class="select-dropdown" onchange="updateDataOption()">
<option value="none">希望しない(印刷物のみ納品)</option>
<option value="pdf">印刷用PDFデータをお渡し (+5,000円)</option>
<option value="ai">Illustratorデータ(アウトライン済) (+15,000円)</option>
<option value="ai_edit">Illustratorデータ(編集可能) (+30,000円)</option>
</select>
<div class="price-display-wrapper">
<div class="price-label">概算費用(税込)</div>
<div class="price-display">
¥<span id="final-price" style="font-size: inherit; color: inherit; font-family: inherit; margin-left: 0; font-weight: inherit;">0</span><span class="price-suffix">〜</span>
</div>
<label class="discount-option">
<input type="checkbox" id="local-discount" onchange="toggleLocalDiscount()">
地域おこし・地方活性化の取り組みなら<strong>-20%</strong>
</label>
<div class="price-note">
※その他、用紙・加工・特急・工数などの要因により変動する場合がございます。詳細はお問い合わせ後に改めてご案内いたします。
</div>
</div>
</div>
<script>
const CONFIG = {
PAPER_SURCHARGE: 1650,
DATA_FEES: {
none: 0,
pdf: 5000,
ai: 15000,
ai_edit: 30000
}
};
const DESIGN_PRICE = {
A3: 25000, A2: 30000, A1: 40000, A0: 50000,
B3: 25000, B2: 35000, B1: 45000, B0: 60000
};
const PRICE_DATA = {
A3: { 10: 31770, 50: 33180, 90: 34590, 100: 36290, 500: 38010, 1000: 40180, 3000: 50740 },
A2: { 10: 42540, 50: 43340, 100: 44320, 300: 45590, 3000: 69950 },
A1: { 10: 52410, 50: 53820, 100: 55590, 300: 57770, 3000: 100160 },
A0: { 10: 159020, 100: 176580, 1000: 229530 },
B3: { 10: 39460, 50: 40060, 100: 40800, 300: 41790, 3000: 60210 },
B2: { 10: 46840, 50: 47890, 100: 49220, 300: 50890, 3000: 82700 },
B1: { 10: 63470, 50: 65560, 100: 68190, 300: 71430, 3000: 128420 },
B0: { 10: 220940, 100: 247200, 1000: 326290 }
};
let state = {
serviceType: 'design_print',
size: 'A3',
paper: 'coat',
qty: 10,
dataDelivery: 'none',
isLocalDiscount: false // 割引チェック状態
};
let currentDisplayPrice = 0;
let animationId = null;
function updateServiceType() {
state.serviceType = document.getElementById('service-type').value;
const printOptionsWrapper = document.getElementById('print-options-wrapper');
const dataDeliverySelect = document.getElementById('data-delivery');
if (state.serviceType === 'design_only') {
printOptionsWrapper.classList.add('disabled-section');
if (state.dataDelivery === 'none') {
dataDeliverySelect.value = 'pdf';
state.dataDelivery = 'pdf';
}
dataDeliverySelect.options[0].disabled = true;
} else {
printOptionsWrapper.classList.remove('disabled-section');
dataDeliverySelect.options[0].disabled = false;
}
calculate();
}
function updateState(key, val) {
state[key] = val;
if (key === 'size') {
const maxQty = (state.size === 'A0' || state.size === 'B0') ? 1000 : 3000;
if (state.qty > maxQty) {
state.qty = maxQty;
updateQtyUI();
}
}
updateButtonActiveStates();
calculate();
}
function updateButtonActiveStates() {
const sizeBtns = document.getElementById('size-selector').querySelectorAll('.select-btn');
sizeBtns.forEach(b => {
b.classList.toggle('active', b.innerText === state.size);
});
const paperMap = { 'coat': 'コート紙', 'matte': 'マットコート紙', 'fine': '上質紙' };
const paperBtns = document.getElementById('paper-selector').querySelectorAll('.select-btn');
paperBtns.forEach(b => {
b.classList.toggle('active', b.innerText === paperMap[state.paper]);
});
}
function updateDataOption() {
state.dataDelivery = document.getElementById('data-delivery').value;
calculate();
}
// 割引チェックボックスの切り替え
function toggleLocalDiscount() {
state.isLocalDiscount = document.getElementById('local-discount').checked;
calculate();
}
function stepQuantity(direction) {
let step = 10;
if (state.qty >= 100 && direction > 0) step = 100;
if (state.qty > 100 && direction < 0) step = 100;
let next = state.qty + (direction * step);
if (next < 10) next = 10;
const max = (state.size === 'A0' || state.size === 'B0') ? 1000 : 3000;
if (next > max) next = max;
state.qty = next;
updateQtyUI();
}
function updateQtyUI() {
document.getElementById('qty-val').innerHTML = `${state.qty.toLocaleString()}部`;
calculate();
}
function interpolate(table, qty) {
const points = Object.keys(table).map(Number).sort((a, b) => a - b);
if (table[qty]) return table[qty];
if (qty <= points[0]) return table[points[0]];
if (qty >= points[points.length - 1]) return table[points[points.length - 1]];
let lower = points[0], upper = points[points.length - 1];
for (let i = 0; i < points.length - 1; i++) {
if (qty > points[i] && qty < points[i+1]) {
lower = points[i];
upper = points[i+1];
break;
}
}
const ratio = (qty - lower) / (upper - lower);
return table[lower] + (table[upper] - table[lower]) * ratio;
}
function calculate() {
let base = 0;
if (state.serviceType === 'design_only') {
base = DESIGN_PRICE[state.size];
} else {
const table = PRICE_DATA[state.size];
base = interpolate(table, state.qty);
if (state.paper !== 'coat') {
base += CONFIG.PAPER_SURCHARGE;
}
}
base += CONFIG.DATA_FEES[state.dataDelivery];
// ▼ 地域活性化割引の適用(20%オフ)
if (state.isLocalDiscount) {
base = base * 0.8;
}
// 10円単位で切り上げ
const final = Math.ceil(base / 10) * 10;
animatePrice(final, 400);
}
function animatePrice(targetPrice, duration) {
const el = document.getElementById('final-price');
const startPrice = currentDisplayPrice;
const startTime = performance.now();
if (animationId) cancelAnimationFrame(animationId);
function update(currentTime) {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
const ease = progress === 1 ? 1 : 1 - Math.pow(2, -10 * progress);
const current = Math.floor(startPrice + (targetPrice - startPrice) * ease);
el.innerText = current.toLocaleString();
currentDisplayPrice = current;
if (progress < 1) {
animationId = requestAnimationFrame(update);
} else {
el.innerText = targetPrice.toLocaleString();
}
}
animationId = requestAnimationFrame(update);
}
// 初期化実行
updateServiceType();
updateButtonActiveStates();
updateQtyUI();
</script>
</body>
</html>