Хочу предложить модуль ежедневных призов.
В базе данных
Создаем БД запросом
Файл presents.php в корке сайта
Файл стилей
Файл getPresents
По настройкам
$presentDay = [rand(10, 14), rand(20, 30), rand(30, 40), rand(40, 50), rand(6, 9), rand(9, 12)]; рандомное количество призов. первые 4 это в обычной валюте. остальные в сз. это можно изменить в $winnerIndex <= 3.
Все выглядит в виде "колеса фортуны"
В базе данных
Вам необходимо зарегистрироваться для просмотра ссылок
создать поле "present" типа int по умолчанию ставим 0.Создаем БД запросом
SQL:
CREATE TABLE `presentDay` (
`id` int NOT NULL,
`login` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`stop` tinyint(1) NOT NULL DEFAULT '0',
`0` tinyint NOT NULL,
`1` tinyint NOT NULL,
`2` tinyint NOT NULL,
`3` tinyint NOT NULL,
`4` tinyint NOT NULL,
`5` tinyint NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
ALTER TABLE `presentDay`
ADD PRIMARY KEY (`id`);
ALTER TABLE `presentDay`
MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=1;
COMMIT;
Файл presents.php в корке сайта
PHP:
<?
require_once($_SERVER['DOCUMENT_ROOT'].'/inc/db_connect.php');
$title = 'Подарок';
include($_SERVER['DOCUMENT_ROOT']."/int/header.php");
?>
<link rel="stylesheet" type="text/css" href="/img/css/presents.css?v2">
<p style="text-align: center;">
<input type="button" class="input" value="Назад" onclick="window.location.href=`main.php`">
</p>
<div class="body">
<div class="deal-wheel">
<ul class="spinner"></ul>
<div class="ticker"></div>
<?
if($stat["present"] > time())
echo'<button class="btn-spin" disabled>Раз в сутки</button>';
else
echo'<button class="btn-spin">Испытай удачу</button>';
?>
</div>
<script>
const wheel = document.querySelector(".deal-wheel");
const spinner = wheel.querySelector(".spinner");
const trigger = wheel.querySelector(".btn-spin");
const ticker = wheel.querySelector(".ticker");
let prizes = [];
let prizeSlice = 0;
let prizeNodes;
let rotation = 0;
let currentSlice = 0;
let tickerAnim;
// Загружаем призы при старте
window.addEventListener("DOMContentLoaded", async () => {
const response = await fetch("getPresents.php?action=load");
const data = await response.json();
prizes = data.prizes;
drawWheel();
});
function drawWheel() {
prizeSlice = 360 / prizes.length;
const prizeOffset = Math.floor(180 / prizes.length);
spinner.innerHTML = '';
spinner.setAttribute("style", `background: conic-gradient(
from -90deg,
${prizes
.map(({ color }, i) => `${color} 0 ${(100 / prizes.length) * (prizes.length - i)}%`)
.reverse()
});`
);
prizes.forEach(({ text }, i) => {
const rotation = ((prizeSlice * i) * -1) - prizeOffset;
spinner.insertAdjacentHTML(
"beforeend",
`<li class="prize" style="--rotate: ${rotation}deg">
<span class="text">${text}</span>
</li>`
);
});
prizeNodes = wheel.querySelectorAll(".prize");
}
function runTickerAnimation() {
const values = getComputedStyle(spinner).transform.split("(")[1].split(")")[0].split(",");
const a = values[0], b = values[1];
let rad = Math.atan2(b, a);
if (rad < 0) rad += (2 * Math.PI);
const angle = Math.round(rad * (180 / Math.PI));
const slice = Math.floor(angle / prizeSlice);
if (currentSlice !== slice) {
ticker.style.animation = "none";
setTimeout(() => ticker.style.animation = null, 10);
currentSlice = slice;
}
tickerAnim = requestAnimationFrame(runTickerAnimation);
}
trigger.addEventListener("click", async () => {
trigger.disabled = true;
const response = await fetch("getPresents.php?action=spin");
const data = await response.json();
rotation = data.winnerIndex;
trigger.disabled = true;
trigger.innerHTML = data.btn;
prizeNodes.forEach(p => p.classList.remove("selected"));
wheel.classList.add("is-spinning");
spinner.style.setProperty("--rotate", rotation);
ticker.style.animation = "none";
runTickerAnimation();
});
spinner.addEventListener("transitionend", () => {
cancelAnimationFrame(tickerAnim);
rotation %= 360;
wheel.classList.remove("is-spinning");
spinner.style.setProperty("--rotate", rotation);
const selected = Math.floor(rotation / prizeSlice);
prizeNodes[selected].classList.add("selected");
});
</script>
</div>
Файл стилей
CSS:
.input {
width: 250px;
background-color: #691d05;
border-radius: 5px;
border: 1px solid #aaa;
color: cornsilk;
font-size: 12px;
padding: 3px;
}
.input:focus, .input:hover {
background-color: #613122;
color: #e5d597;
box-shadow: 0 0 5px rgba(255,25,33,.9);
-moz-box-shadow:0 0 5px rgba(255,25,33,.9);
-webkit-box-shadow:0 0 5px rgba(255,25,33,.9);
}
.input:disabled {
background-color: #4f3127;
color: #e5d597;
box-shadow: 0 0 5px rgba(0,0,0,.9);
-moz-box-shadow:0 0 5px rgba(0,0,0,.9);
-webkit-box-shadow:0 0 5px rgba(0,0,0,.9);
cursor: no-drop;
}
.input:disabled {
background-color: #4f3127;
color: #e5d597;
box-shadow: 0 0 5px rgba(0,0,0,.9);
-moz-box-shadow:0 0 5px rgba(0,0,0,.9);
-webkit-box-shadow:0 0 5px rgba(0,0,0,.9);
cursor: no-drop;
}
.input::placeholder {
color: cornsilk;
opacity: 1;
font-style: italic;
font-size: 12px;
}
.input::-moz-placeholder {
color: cornsilk;
opacity: 1;
font-style: italic;
font-size: 12px;
}
.input::-webkit-input-placeholder {
color: cornsilk;
opacity: 1;
font-style: italic;
font-size: 12px;
}
/* общий блок для всех элементов */
.deal-wheel {
/* задаём переменные блока */
/* размеры колеса */
--size: clamp(250px, 80vmin, 700px);
/* настройки яркости и заливки фона секторов */
--lg-hs: 0 3%;
--lg-stop: 50%;
--lg: linear-gradient(
hsl(var(--lg-hs) 0%) 0 var(--lg-stop),
hsl(var(--lg-hs) 20%) var(--lg-stop) 100%
);
/* добавляем позиционирование относительно других элементов */
position: relative;
/* подключаем сетку */
display: grid;
grid-gap: calc(var(--size) / 20);
/* выравниваем содержимое блока по центру */
align-items: center;
/* задаём имена областей внутри сетки */
grid-template-areas:
"spinner"
"trigger";
/* устанавливаем размер шрифта */
font-size: calc(var(--size) / 21);
}
/* всё, что относится ко внутренним элементам главного блока, будет находиться в области сетки с названием spinner */
.deal-wheel > * {
grid-area: spinner;
}
/* сам блок и кнопка будут находиться в области сетки с названием trigger и будут выровнены по центру */
.deal-wheel .btn-spin {
grid-area: trigger;
justify-self: center;
}
/* сектор колеса */
.spinner {
/* добавляем относительное позиционирование */
position: relative;
/* подключаем сетку */
display: grid;
/* выравниваем всё по центру */
align-items: center;
/* добавляем элемент в сетку */
grid-template-areas: "spinner";
/* устанавливаем размеры */
width: var(--size);
height: var(--size);
/* поворачиваем элемент */
transform: rotate(calc(var(--rotate, 25) * 1deg));
/* рисуем круглую обводку, а всё, что не поместится, — будет скрыто за кругом */
border-radius: 50%;
}
/* всё, что внутри этого блока, будет находиться в области сетки с названием spinner */
.spinner * {
grid-area: spinner;
}
/* текст на секторах */
.prize {
/* включаем «гибкую» вёрстку */
display: flex;
align-items: center;
/* задаём отступы от краёв блока */
padding: 0 calc(var(--size) / 6) 0 calc(var(--size) / 20);
/* устанавливаем размеры */
width: 50%;
height: 50%;
/* устанавливаем координаты, относительно которых будем вращать текст */
transform-origin: center right;
/* поворачиваем текст */
transform: rotate(var(--rotate));
/* запрещаем пользователю выделять мышкой текст на секторах */
user-select: none;
}
/* язычок */
.ticker {
/* добавляем относительное позиционирование */
position: relative;
/* устанавливаем размеры */
left: calc(var(--size) / -15);
width: calc(var(--size) / 10);
height: calc(var(--size) / 20);
/* фон язычка */
background: var(--lg);
/* делаем так, чтобы язычок был выше колеса */
z-index: 1;
/* форма язычка */
clip-path: polygon(20% 0, 100% 50%, 20% 100%, 0% 50%);
/* устанавливаем точку, относительно которой будет вращаться язычок при движении колеса */
transform-origin: center left;
}
/* кнопка запуска колеса */
.btn-spin {
color: white;
background: black;
border: none;
/* берём размер шрифта такой же, как в колесе */
font-size: inherit;
/* добавляем отступы от текста внутри кнопки */
padding: 0.9rem 2rem 1rem;
/* скругляем углы */
border-radius: 0.5rem;
/* меняем внешний вид курсора над кнопкой на руку*/
cursor: pointer;
}
/* если кнопка нажата и неактивна */
.btn-spin:disabled {
/* меняем внешний вид курсора */
cursor: progress;
/* делаем кнопку полупрозрачной */
opacity: 0.25;
}
/* анимация вращения */
.is-spinning .spinner {
transition: transform 8s cubic-bezier(0.1, -0.01, 0, 1);
}
/* анимация движения язычка */
.is-spinning .ticker {
animation: tick 700ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
/* эффект, когда колесо задевает язычок при вращении */
@keyframes tick {
40% {
/* чуть поворачиваем язычок наверх в середине анимации */
transform: rotate(-12deg);
}
}
/* анимируем выпавший сектор */
.prize.selected .text {
/* делаем текст белым */
color: white;
/* настраиваем длительность анимации */
animation: selected 800ms ease;
}
/* настраиваем анимацию текста на выпавшем секторе по кадрам */
@keyframes selected {
/* что происходит на 25% от начала анимации */
25% {
/* увеличиваем текст в 1,25 раза */
transform: scale(1.25);
/* добавляем тексту тень */
text-shadow: 1vmin 1vmin 0 hsla(0 0% 0% / 0.1);
}
40% {
transform: scale(0.92);
text-shadow: 0 0 0 hsla(0 0% 0% / 0.2);
}
60% {
transform: scale(1.02);
text-shadow: 0.5vmin 0.5vmin 0 hsla(0 0% 0% / 0.1);
}
75% {
transform: scale(0.98);
}
85% {
transform: scale(1);
}
}
Файл getPresents
Код:
<?php
require_once($_SERVER['DOCUMENT_ROOT'].'/inc/db_connect.php');
$presentDay = mysql_query(false, "SELECT * FROM `presentDay` WHERE `login` = ? AND `stop` = 0", "s", [$stat['user']]);
if(mysql_num_rows($presentDay) == 0) {
$presentDay = [rand(10, 14), rand(20, 30), rand(30, 40), rand(40, 50), rand(6, 9), rand(9, 12)];
mysql_query("INSERT INTO `presentDay` (`login`, `0`, `1`, `2`, `3`, `4`, `5`) VALUES ('".$stat['user']."', ".$presentDay[0].", ".$presentDay[1].", ".$presentDay[2].", ".$presentDay[3].", ".$presentDay[4].", ".$presentDay[5].")");
}
else
$presentDay = mysql_fetch_array($presentDay);
header('Content-Type: application/json');
$action = $_GET['action'] ?? 'load';
$prizes = [
["price" => $presentDay[0], "text" => $presentDay[0].' зм', "color" => "#f44336", "weight" => 30],
["price" => $presentDay[1], "text" => $presentDay[1].' зм', "color" => "#e91e63", "weight" => 25],
["price" => $presentDay[2], "text" => $presentDay[2].' зм', "color" => "#9c27b0", "weight" => 20],
["price" => $presentDay[3], "text" => $presentDay[3].' зм', "color" => "#3f51b5", "weight" => 15],
["price" => $presentDay[4], "text" => $presentDay[4].' сз', "color" => "#2196f3", "weight" => 5],
["price" => $presentDay[5], "text" => $presentDay[5].' сз', "color" => "#009688", "weight" => 5],
];
if($action === 'load') {
echo json_encode(["prizes" => $prizes]);
exit;
}
if($action === 'spin') {
if($stat["present"] > time()) {
echo json_encode(["btn" => 'Раз в сутки', "winnerIndex" => 0]);
exit;
}
$weights = array_column($prizes, 'weight');
$totalWeight = array_sum($weights);
$rand = mt_rand(1, $totalWeight);
$cumulative = 0;
$winnerIndex = 0;
foreach ($weights as $i => $weight) {
$cumulative += $weight;
if ($rand <= $cumulative) {
$winnerIndex = $i;
break;
}
}
mysql_query("UPDATE `presentDay` set `stop` = 1 WHERE `login` = '".$stat['user']."' AND `stop` = 0");
if($winnerIndex <= 3)
mysql_query("UPDATE `players` set `credits` = `credits` + ".$prizes[$winnerIndex]['price'].", `present` = ".(time() + 86400)." WHERE `id` = ".$stat['id']."");
else
mysql_query("UPDATE `players` set `slit` = `slit` + ".$prizes[$winnerIndex]['price'].", `present` = ".(time() + 86400)." WHERE `id` = ".$stat['id']."");
echo json_encode(["btn" => 'Раз в сутки', "winnerIndex" => $winnerIndex * 60 + rand(6, 16) * 360 + rand(20, 40)]);
exit;
}
?>
По настройкам
$presentDay = [rand(10, 14), rand(20, 30), rand(30, 40), rand(40, 50), rand(6, 9), rand(9, 12)]; рандомное количество призов. первые 4 это в обычной валюте. остальные в сз. это можно изменить в $winnerIndex <= 3.
Все выглядит в виде "колеса фортуны"