<特徴>
1.ecコンシェルとGoogle Tag Manager(GTM)を組み合わせることで、システム開発なしで「本格的なルーレット」をサイト上に実装できます。
2.「当たりの項目数」「当選確率」「当たりの名称」「盤面の色」「遷移先URL」はecコンシェルの画面から簡単に変更可能です。設定した項目の数に合わせて、ルーレットの盤面が自動的に分割・生成されます。一度GTMを設定すれば、以降はGTMで作成したタグを変更する必要はありません。
3.ユーザーが自ら「START」を押してアタリを引き当てるため、通常のバナーよりも高いクリック率やクーポン利用率が期待できます。
<注意事項>
マニュアル通りのコピー&ペーストで動作するように設計されていますが、指定のIDやHTML構造を変更・削除すると動作しなくなりますのでご注意ください。
必ずテスト配信にて「タップ時にアニメーションが動作するか」「確率通りに当たるか」「正しいURLに遷移するか」など想定通り動作するかを確認してから本番公開をお願いいたします。
<使用手順1:ecコンシェル側の設定>
1.キャンペーン設定画面で「なにを」>「ウィジェットを登録する」をクリックし、テンプレートから「J-001 タイトル+詳細テキスト」を選択します。
2.「テキスト」メニューを開き、入力タイプを「HTML」に変更します。
3.以下のHTMLコードをコピーして貼り付けます。
上部の
<div class="ecc_prize"> の行をキャンペーンに合わせて書き換えます。※この行数を増減させるだけで、自動的に盤面が2分割や6分割などに調整されます。
・
data-name="〇〇":結果画面に出るテキスト・
data-label="〇〇":ルーレット盤面に表示される短いテキスト(<br>で改行可能)・
data-prob="〇〇":当選確率(%)。※すべての項目の合計が必ず「100」になるようにしてください。・
data-url="〇〇":当選後にタップしたときの遷移先URL・
data-bg="〇〇":盤面マスの背景色(#ffffffなどのカラーコード。省略可)・
data-text="〇〇":盤面マスの文字色(#333333などのカラーコード。省略可)<div style="text-align: center !important; font-weight: bold;">限定クーポンが当たる!</div><!-- ▼ 当選設定(ここの行数を増減させるだけで自動的に盤面マス数が変更されます) ▼ -->
<div id="ecc_roulette_config" style="display: none !important;">
<div class="ecc_prize" data-name="【大当】50%OFF" data-label="50%<br>OFF" data-prob="10" data-url="https://example.com/50off" data-bg="#b71c1c" data-text="#ffffff"></div>
<div class="ecc_prize" data-name="送料無料クーポン" data-label="送料<br>無料" data-prob="30" data-url="https://example.com/free" data-bg="#ffffff" data-text="#b71c1c"></div>
<div class="ecc_prize" data-name="10%OFFクーポン" data-label="10%<br>OFF" data-prob="30" data-url="https://example.com/10off" data-bg="#b71c1c" data-text="#ffffff"></div>
<div class="ecc_prize" data-name="500円引クーポン" data-label="500円<br>引" data-prob="30" data-url="https://example.com/500" data-bg="#ffffff" data-text="#b71c1c"></div>
</div>
<!-- ▼ ルーレット枠全体 ▼ -->
<div id="ecc_roulette_wrapper" style="position: relative !important; width: 100% !important; max-width: 320px !important; margin: 0 auto !important; padding: 25px 15px !important; text-align: center !important; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif !important; background: #fff !important; border-radius: 8px !important; box-shadow: 0 4px 15px rgba(0,0,0,0.15) !important; border: 2px solid #b71c1c !important; box-sizing: border-box !important;">
<!-- ▼ ルーレット上部の説明文(自由に書き換えてください) ▼ -->
<div style="font-size: 12px !important; color: #666 !important; margin-bottom: 20px !important;">STARTをタップして回そう!</div>
<!-- ▼ 盤面やボタンの構造(※ここから下は絶対に書き換えないでください) ▼ -->
<div style="position: relative !important; width: 200px !important; height: 200px !important; margin: 0 auto 15px !important;">
<div style="position: absolute !important; top: -12px !important; left: 50% !important; transform: translateX(-50%) !important; width: 0 !important; height: 0 !important; border-left: 15px solid transparent !important; border-right: 15px solid transparent !important; border-top: 22px solid #333 !important; z-index: 10 !important;"></div>
<!-- 盤面 -->
<div id="ecc_roulette_board" style="width: 100% !important; height: 100% !important; border-radius: 50% !important; border: 4px solid #333 !important; box-sizing: border-box !important; position: relative !important; overflow: hidden !important; transform: rotate(0deg) !important;"></div>
<!-- STARTボタン -->
<div id="ecc_roulette_btn" style="position: absolute !important; top: 50% !important; left: 50% !important; transform: translate(-50%, -50%) !important; width: 60px !important; height: 60px !important; background: #333 !important; color: #fff !important; border-radius: 50% !important; display: flex !important; align-items: center !important; justify-content: center !important; font-weight: bold !important; font-size: 14px !important; cursor: pointer !important; border: 3px solid #fff !important; z-index: 11 !important; box-shadow: 0 2px 6px rgba(0,0,0,0.3) !important;">START</div>
</div>
<!-- 結果表示エリア -->
<div id="ecc_roulette_result" style="display: none !important;"></div>
</div>4.「表示設定(リンク先URL)」にデフォルトのキャンペーンURL等を設定し、保存します。
<使用手順2:GTM側の設定>
1.Google Tag Manager(GTM)の管理画面で「タグ」>「新規」をクリックします。
2.タグタイプを「カスタム HTML」に設定します。
3.以下のスクリプトをコピーして貼り付けます。(※コードの編集は一切不要です)
<script>
(function() {
var css = '@keyframes eccRouletteInserted { from { opacity: 0.99; } to { opacity: 1; } } ' +
'#ecc_roulette_wrapper, [data-ecc-wrapper="roulette"] { animation: eccRouletteInserted 0.001s; }';
var style = document.createElement('style');
style.type = 'text/css';
if (style.styleSheet) style.styleSheet.cssText = css;
else style.appendChild(document.createTextNode(css));
document.head.appendChild(style);
window._ecc_roulette_memory = window._ecc_roulette_memory || {};
if (!window._ecc_roulette_clear_timer_set) {
window._ecc_roulette_clear_timer_set = true;
setInterval(function() {
var container = document.querySelector('.ec-concier-widget-container, .ec-concier-frame');
if (!container) window._ecc_roulette_memory = {};
}, 1000);
}
function initRoulette(wrapper) {
if (!wrapper || wrapper.getAttribute('data-ecc-init') === 'true') return;
wrapper.setAttribute('data-ecc-init', 'true');
wrapper.setAttribute('data-ecc-wrapper', 'roulette');
var config = null;
var pNode = wrapper.parentNode;
if (pNode) config = pNode.querySelector('[id="ecc_roulette_config"]');
if (!config) {
var prev = wrapper.previousElementSibling;
if (prev && prev.id === 'ecc_roulette_config') config = prev;
}
if (!config) config = document.getElementById('ecc_roulette_config');
if (!config) return;
var prizeEls = config.querySelectorAll('.ecc_prize');
var itemCount = prizeEls.length;
if (itemCount === 0) return;
var board = wrapper.querySelector('[id="ecc_roulette_board"]');
var btn = wrapper.querySelector('[id="ecc_roulette_btn"]');
if (!board || !btn) return;
var prizes = [];
var campaignId = "";
board.innerHTML = '';
var anglePerSlice = 360 / itemCount;
var gradientParts = [];
var defaultBgs = ['#b71c1c', '#ffffff', '#F5B01D', '#ffffff', '#333333', '#e0e0e0'];
var defaultTexts = ['#ffffff', '#b71c1c', '#ffffff', '#b71c1c', '#ffffff', '#333333'];
for (var i = 0; i < itemCount; i++) {
var el = prizeEls[i];
var name = el.getAttribute('data-name');
campaignId += name || "";
var labelText = el.getAttribute('data-label') || '';
var bgCol = el.getAttribute('data-bg') || defaultBgs[i % defaultBgs.length];
var txtCol = el.getAttribute('data-text') || defaultTexts[i % defaultTexts.length];
prizes.push({
name: name,
label: labelText,
probability: parseFloat(el.getAttribute('data-prob')),
url: el.getAttribute('data-url'),
index: i
});
var startAngle = i * anglePerSlice;
var endAngle = (i + 1) * anglePerSlice;
gradientParts.push(bgCol + ' ' + startAngle + 'deg ' + endAngle + 'deg');
var sliceCenter = startAngle + (anglePerSlice / 2);
var textDiv = document.createElement('div');
textDiv.innerHTML = labelText;
textDiv.style.setProperty('position', 'absolute', 'important');
textDiv.style.setProperty('width', '75px', 'important');
textDiv.style.setProperty('height', '50px', 'important');
textDiv.style.setProperty('top', '75px', 'important');
textDiv.style.setProperty('left', '96px', 'important');
textDiv.style.setProperty('transform-origin', '0 50%', 'important');
textDiv.style.setProperty('transform', 'rotate(' + (sliceCenter - 90) + 'deg) translate(22px, 0)', 'important');
textDiv.style.setProperty('color', txtCol, 'important');
textDiv.style.setProperty('font-weight', 'bold', 'important');
textDiv.style.setProperty('font-size', '12px', 'important');
textDiv.style.setProperty('line-height', '1.3', 'important');
textDiv.style.setProperty('display', 'flex', 'important');
textDiv.style.setProperty('align-items', 'center', 'important');
textDiv.style.setProperty('justify-content', 'center', 'important');
textDiv.style.setProperty('text-align', 'center', 'important');
board.appendChild(textDiv);
}
campaignId = campaignId.replace(/[^a-zA-Z0-9]/g, '');
board.style.setProperty('background', 'conic-gradient(' + gradientParts.join(', ') + ')', 'important');
var resultArea = wrapper.querySelector('[id="ecc_roulette_result"]');
if (!resultArea) {
resultArea = document.createElement('div');
resultArea.id = 'ecc_roulette_result';
wrapper.appendChild(resultArea);
}
var savedIndex = window._ecc_roulette_memory[campaignId] !== undefined ? window._ecc_roulette_memory[campaignId] : null;
function showResult(hitItem) {
btn.style.setProperty('display', 'none', 'important');
resultArea.innerHTML =
'<span style="font-size:14px!important;color:#666!important;font-weight:bold!important;display:block!important;">おめでとうございます!</span>' +
'<span style="font-size:18px!important;font-weight:bold!important;color:#b71c1c!important;display:block!important;margin-top:5px!important;">' + hitItem.name + '</span>' +
'<span style="font-size:12px!important;color:#000!important;display:block!important;margin-top:10px!important;text-decoration:underline!important;cursor:pointer;">ここをタップして獲得!</span>';
resultArea.style.setProperty('position', 'absolute', 'important');
resultArea.style.setProperty('top', '55%', 'important');
resultArea.style.setProperty('left', '50%', 'important');
resultArea.style.setProperty('transform', 'translate(-50%, -50%)', 'important');
resultArea.style.setProperty('width', '85%', 'important');
resultArea.style.setProperty('box-sizing', 'border-box', 'important');
resultArea.style.setProperty('z-index', '100', 'important');
resultArea.style.setProperty('background', 'rgba(255, 243, 243, 0.95)', 'important');
resultArea.style.setProperty('box-shadow', '0 4px 15px rgba(0,0,0,0.2)', 'important');
resultArea.style.setProperty('display', 'block', 'important');
resultArea.style.setProperty('padding', '15px', 'important');
resultArea.style.setProperty('border-radius', '8px', 'important');
resultArea.style.setProperty('border', '1px dashed #b71c1c', 'important');
}
if (savedIndex !== null && prizes[savedIndex]) {
board.style.setProperty('opacity', '0.5', 'important');
showResult(prizes[savedIndex]);
} else {
resultArea.style.setProperty('display', 'none', 'important');
}
var isAnimating = false;
var interceptor = function(e) {
if (window._ecc_ignore_interceptor) return;
if (!document.body.contains(wrapper)) {
window.removeEventListener(e.type, interceptor, true);
return;
}
var mainBanner = wrapper;
var pNode = wrapper.parentNode;
while(pNode && pNode.tagName !== 'BODY' && pNode.tagName !== 'HTML') {
if (typeof pNode.className === 'string' && pNode.className.indexOf('ec-concier-layout-wrapper') !== -1) {
mainBanner = pNode; break;
}
pNode = pNode.parentNode;
}
if (e.target === mainBanner || mainBanner.contains(e.target)) {
e.stopPropagation();
if (e.type === 'click') {
e.preventDefault();
var isStart = (e.target === btn || btn.contains(e.target));
var isRes = (resultArea.style.display !== 'none' && (e.target === resultArea || resultArea.contains(e.target)));
if (isStart) {
if (isAnimating || savedIndex !== null) return;
isAnimating = true;
var rand = Math.random() * 100, cumulative = 0, hit = prizes[prizes.length - 1];
for (var j = 0; j < prizes.length; j++) {
cumulative += prizes[j].probability;
if (rand <= cumulative) { hit = prizes[j]; break; }
}
window._ecc_roulette_memory[campaignId] = hit.index;
savedIndex = hit.index;
btn.style.setProperty('background', '#999', 'important');
btn.innerText = '回転中...';
board.style.setProperty('transition', 'transform 3s cubic-bezier(0.25, 0.1, 0.25, 1)', 'important');
var hitCenter = hit.index * anglePerSlice + (anglePerSlice / 2);
var offset = (Math.random() * (anglePerSlice * 0.6)) - (anglePerSlice * 0.3);
var finalDegree = 1800 + (360 - hitCenter) + offset;
setTimeout(function() {
board.style.setProperty('transform', 'rotate(' + finalDegree + 'deg)', 'important');
}, 50);
setTimeout(function(){
showResult(hit);
isAnimating = false;
}, 3050);
} else if (isRes) {
if (savedIndex !== null && !isAnimating) {
window._ecc_ignore_interceptor = true;
var targetPrize = prizeEls[savedIndex];
if (targetPrize) { try { targetPrize.click(); } catch(err){} }
window._ecc_ignore_interceptor = false;
var destUrl = prizes[savedIndex].url;
if (destUrl && destUrl !== 'javascript:void(0)') {
setTimeout(function(){ window.location.href = destUrl; }, 100);
}
}
}
}
}
};
if (!wrapper.hasAttribute('data-click-bound')) {
wrapper.setAttribute('data-click-bound', 'true');
['mousedown', 'mouseup', 'touchstart', 'touchend', 'pointerdown', 'pointerup', 'click'].forEach(function(ev) {
window.addEventListener(ev, interceptor, true);
});
}
}
function scanAndInit() {
var wrappers = document.querySelectorAll('[id="ecc_roulette_wrapper"], [data-ecc-wrapper="roulette"]');
for(var i=0; i<wrappers.length; i++) initRoulette(wrappers[i]);
}
var insertListener = function(event) {
if (event.animationName === 'eccRouletteInserted') scanAndInit();
};
document.addEventListener('animationstart', insertListener, false);
document.addEventListener('webkitAnimationStart', insertListener, false);
function startObserver() {
if (!document.body) { setTimeout(startObserver, 100); return; }
var scanTimer;
var observer = new MutationObserver(function() { clearTimeout(scanTimer); scanTimer = setTimeout(function() { scanAndInit(); }, 200); });
observer.observe(document.body, { childList: true, subtree: true });
setTimeout(scanAndInit, 500);
}
startObserver();
scanAndInit();
})();
</script>【推奨設定】
ecコンシェルのキャンペーンを配信する予定のページにのみスクリプトが読み込まれるよう設定することをおすすめします。
・トリガーのタイプ:ページビュー
・発生場所:一部のページビュー
・条件例:Page Path が /example から始まる等ルーレットウィジェットを配信する予定のページを指定
※サイト全域で一律稼働させたい場合は「All Pages」を選択しても問題ありませんが、サイトの表示速度や運用ルールの観点から、必要なページに絞ることを推奨します。