├── demo.png ├── index.html ├── artag.js ├── artag.css └── README.md /demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/artengin/ArPic/HEAD/demo.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Скрипт ArPic - интерактивные метки на изображении 6 | 7 | 8 | 9 | 10 | Компонент 11 | 12 |
13 |
14 |
15 | 16 | 17 |
18 |
19 |
20 |
21 | 22 | 23 | 24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /artag.js: -------------------------------------------------------------------------------- 1 | /* Вызов функции 2 | if (document.querySelector('#ar-inter')) { 3 | arInter('#ar-inter', 2560, 2005); 4 | } 5 | */ 6 | 7 | function arInter(doc, widthImage, heightImage, countLabels = 50) { 8 | 9 | //Создаем классы для кнопок на изображении 10 | let arrClassName = []; 11 | createClassBtnImg(); 12 | 13 | function createClassBtnImg() { 14 | for (let i = 0; i <= countLabels; i++) { 15 | arrClassName.push('ar-btn' + i); 16 | } 17 | } 18 | 19 | let arBtnImgArray = []; 20 | let arBtnTitleArray = []; 21 | let arDescArray = []; 22 | let newHeight; 23 | 24 | //Подключение к DOM 25 | const arInterBlock = document.querySelector(doc); 26 | const arImg = arInterBlock.querySelector('.ar-img'); 27 | arImg.className = arrClassName[0] + ' ar-img'; 28 | 29 | if (arImg.querySelectorAll('span')) { 30 | arBtnImgArray = arImg.querySelectorAll('span'); 31 | addClassBtnImg(arBtnImgArray); 32 | } 33 | 34 | if (arInterBlock.querySelector('.ar-btn-title')) { 35 | arBtnTitle = arInterBlock.querySelector('.ar-btn-title'); 36 | arBtnTitleArray = arBtnTitle.querySelectorAll('button'); 37 | addClassBtnImg(arBtnTitleArray); 38 | 39 | } 40 | 41 | if (arInterBlock.querySelector('.ar-desc')) { 42 | arDesc = arInterBlock.querySelector('.ar-desc'); 43 | arDescArray = arDesc.querySelectorAll('div'); 44 | addClassBtnImg(arDescArray); 45 | } 46 | 47 | //Устанавливаем классы и обработчик событий меткам на изображении 48 | function addClassBtnImg(btns) { 49 | let n = 0; 50 | if (btns == arBtnImgArray) { 51 | n = 1; 52 | } 53 | for (i = 0; i < btns.length; i++) { 54 | btns[i].classList.add(arrClassName[n]); 55 | if (btns != arDescArray) { 56 | btns[i].addEventListener('click', (e) =>{ 57 | btnActive(e.target, e.target.classList.value); 58 | }, true); 59 | } 60 | n++; 61 | } 62 | } 63 | 64 | //Вычисляем соотношение пропорций изображения 65 | function calcProportion(width, height) { 66 | newHeight = (height * 100) / width; 67 | } 68 | calcProportion(widthImage, heightImage); 69 | 70 | //Устанавливаем высоту изображения при изменении размера экрана 71 | function setHeightImg(img, difHeight) { 72 | let getStyle = window.getComputedStyle(arImg); 73 | let height = (parseInt(getStyle.width.slice(0, -2)) * difHeight) / 100; 74 | img.style.height = `${height}px`; 75 | } 76 | 77 | window.addEventListener('resize',() => { 78 | setHeightImg(arImg, newHeight); 79 | }); 80 | setHeightImg(arImg, newHeight); 81 | 82 | //Функция сбрасывает состояния 83 | function clearState(elem) { 84 | for (i = 0; i < elem.length; i++) { 85 | if (elem[i].classList.contains("ar-disable")) { 86 | elem[i].classList.remove('ar-disable'); 87 | } else if(elem[i].classList.contains("ar-active")) { 88 | elem[i].classList.remove('ar-active'); 89 | } 90 | } 91 | } 92 | 93 | function clearAll() { 94 | clearState(arBtnImgArray); 95 | clearState(arBtnTitleArray); 96 | clearState(arDescArray); 97 | } 98 | 99 | //Функция добавяет состояния 100 | function addState(elem, target) { 101 | for (i = 0; i < elem.length; i++) { 102 | if(elem[i].classList.contains(target)){ 103 | elem[i].classList.add('ar-active'); 104 | return; 105 | } 106 | } 107 | } 108 | 109 | //Обрабатываем нажатия на кнопки 110 | function btnActive(target, targetClass){ 111 | if (target.classList.contains('ar-btn0')) { 112 | clearAll(); 113 | arInterBlock.classList.remove('ar-active'); 114 | arImg.className = arrClassName[0] + ' ar-img'; 115 | } else if (target.classList.contains('ar-active') && target.parentNode.classList.contains('ar-img')) { 116 | clearAll(); 117 | arInterBlock.classList.remove('ar-active'); 118 | arImg.className = arrClassName[0] + ' ar-img'; 119 | } else if (!target.classList.contains('ar-active')) { 120 | arInterBlock.classList.add('ar-active'); 121 | arImg.className = targetClass + ' ar-img'; 122 | 123 | clearAll() 124 | addState(arBtnImgArray, targetClass); 125 | addState(arBtnTitleArray, targetClass); 126 | addState(arDescArray, targetClass); 127 | } 128 | } 129 | } -------------------------------------------------------------------------------- /artag.css: -------------------------------------------------------------------------------- 1 | 2 | .ar-inter-block{ 3 | width: 100%; 4 | margin: 50px 0; 5 | display: flex; 6 | justify-content: space-between; 7 | font-family: Arial, sans-serif; 8 | } 9 | 10 | .ar-inter-block .left { 11 | width: 62%; 12 | } 13 | 14 | .ar-inter-block .ar-img { 15 | width: 100%; 16 | border-radius: 10px; 17 | background-size: 100%; 18 | background-position-x: center; 19 | background-position-y: center; 20 | position: relative; 21 | transition: all ease-out 1s; 22 | } 23 | 24 | .ar-inter-block .right { 25 | width: 35%; 26 | border-radius: 10px; 27 | background-color: #fff; 28 | color: #333; 29 | box-sizing: border-box; 30 | padding: 30px; 31 | display: flex; 32 | flex-direction: column; 33 | justify-content: space-between; 34 | } 35 | 36 | .ar-inter-block .ar-btn-title { 37 | display: flex; 38 | justify-content: flex-end; 39 | flex-wrap: wrap; 40 | } 41 | 42 | .ar-inter-block .block-title { 43 | display: flex; 44 | flex-wrap: wrap; 45 | justify-content: flex-end; 46 | } 47 | 48 | .ar-inter-block .ar-btn-title button { 49 | background: #fff; 50 | color: #000; 51 | font-size: 16px; 52 | border-radius: 16px; 53 | border: 1px solid #000; 54 | display: block; 55 | margin: 2px 4px; 56 | padding: 8px 8px 6px; 57 | line-height: 12px; 58 | cursor: pointer; 59 | } 60 | 61 | .ar-inter-block .ar-btn-title .ar-btn0 { 62 | position: relative; 63 | padding: 13px; 64 | border-radius: 50%; 65 | } 66 | 67 | .ar-inter-block .ar-btn-title .ar-btn0::after { 68 | content: '→'; 69 | position: absolute; 70 | color: #333; 71 | top: 25%; 72 | left: 12%; 73 | width: 20px; 74 | } 75 | 76 | .ar-inter-block .ar-img span::before{ 77 | position: absolute; 78 | background: #fff; 79 | color: #333; 80 | padding: 4px 5px 3px; 81 | font-size: 13px; 82 | border: 1px solid #333; 83 | border-radius: 5px; 84 | transition: all 0.3s; 85 | opacity: 0; 86 | top: -23px; 87 | left: 23px; 88 | } 89 | 90 | .ar-inter-block .ar-img span:hover::before{ 91 | opacity: 1; 92 | transition: all 0.8s; 93 | } 94 | 95 | .ar-inter-block.ar-active .ar-img span:hover::before{ 96 | opacity: 0; 97 | } 98 | 99 | .ar-inter-block.ar-active .ar-btn-title .ar-btn0 { 100 | background: #333; 101 | color: #fff; 102 | } 103 | 104 | .ar-inter-block.ar-active .ar-btn-title .ar-btn0::after { 105 | content: 'x'; 106 | position: absolute; 107 | top: 22%; 108 | font-size: 18px; 109 | left: 11%; 110 | color: #fff; 111 | width: 20px; 112 | } 113 | 114 | .ar-inter-block .ar-btn-title button:hover { 115 | background: #bababa; 116 | } 117 | 118 | .ar-inter-block .ar-btn-title .ar-active { 119 | background: #333; 120 | color: #fff; 121 | } 122 | 123 | .ar-inter-block .ar-btn-title .ar-active:hover { 124 | background: #333; 125 | color: #fff; 126 | } 127 | 128 | .ar-img span { 129 | display: none; 130 | position: absolute; 131 | border-radius: 50%; 132 | background: rgb(0 0 0 / 39%); 133 | height: 25px; 134 | width: 25px; 135 | box-sizing: border-box; 136 | transition: all 0.8s; 137 | cursor: pointer; 138 | } 139 | 140 | .ar-btn0 span { 141 | display: block !important; 142 | } 143 | 144 | .ar-img .ar-active { 145 | display: block; 146 | left: 90% !important; 147 | bottom: 90% !important; 148 | } 149 | 150 | .ar-img .ar-active::after { 151 | content: "\2014"; 152 | bottom: 1.5px; 153 | left: 11%; 154 | font-size: 19px; 155 | } 156 | 157 | .ar-img span:hover, 158 | .ar-img span:focus { 159 | background: rgb(0 0 0 / 72%); 160 | } 161 | 162 | .ar-img span:after { 163 | content: "+"; 164 | position: absolute; 165 | bottom: -8%; 166 | left: 23%; 167 | font-size: 23px; 168 | color: #fff; 169 | opacity: 1; 170 | font-style: normal; 171 | } 172 | 173 | .ar-inter-block .ar-desc div { 174 | display: none; 175 | } 176 | 177 | .ar-inter-block .ar-desc .ar-btn0 { 178 | display: block; 179 | } 180 | 181 | .ar-inter-block.ar-active .ar-desc .ar-btn0 { 182 | display: none; 183 | } 184 | 185 | .ar-inter-block .ar-desc .ar-active { 186 | display: block; 187 | } 188 | 189 | .ar-inter-block .ar-btn-title button:hover { 190 | background: rgb(0, 0, 0, 20%); 191 | } 192 | 193 | @media (max-width: 992px) { 194 | .ar-inter-block { 195 | flex-wrap: wrap; 196 | margin: 10px 0; 197 | } 198 | 199 | .ar-inter-block .left, 200 | .ar-inter-block .right { 201 | width: 100%; 202 | margin-bottom: 10px; 203 | } 204 | } 205 | 206 | @media (max-width: 650px) { 207 | .ar-img .ar-active { 208 | display: block; 209 | left: 85% !important; 210 | bottom: 85% !important; 211 | } 212 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Скрипт интерактивного изображения с метками | ArPic 2 | 3 | ![Демо](demo.png) 4 | 5 | #### Демо вы можете посмотреть здесь: [DEMO](https://art.osepyan.ru/arpic/) 6 | 7 | *Данный скрипт распространяется под лицензией MIT. Следовательно, его можно использовать бесплатно как в личных, так и в коммерческих проектах.* 8 | 9 | ### Документация: 10 | 11 | Данный скрипт позволяет создавать адаптивные изображения с интерактивными метками. 12 | 13 | Для начала работы необходимо подключить файлы `artag.css` и `artag.js`. 14 | 15 | ### В HTML документе создаем блок с интерактивными метками на изображении: 16 | 17 | ```html 18 |
19 |
20 |
21 | 22 | 23 |
24 |
25 |
26 |
27 | 28 | 29 | 30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | ``` 39 | 40 | - В `id="ar-inter"` прописываем идентификатор блока с интерактивным изображением (например `ar-inter1`, `ar-inter2` ...). 41 | Блоков с интерактивными изображениями может быть несколько на странице, поэтому не забывайте менять `id`. 42 | - Тег `span` отвечает за конкретную метку на изображении (является необязательным), в них не надо ничего записывать. 43 | Вы можете создать до 50 меток (тегов `span`). 44 | - Блок `class="right"` является необязательным. 45 | Вы можете создать только изображение с метками, без заголовков и текстового описания, либо создать изображение с описанием, но без меток. Ничего дополнительно настраивать не придется. 46 | - Тег `button` отвечает за кнопку-заголовок (является необязательным). В тег `button` (начиная со второго по счету) необходимо записать текстовое содержимое. 47 | Самый первый тег `button` является кнопкой возврата к исходному состоянию блока, его не надо редактировать. 48 | - Тег `div` — это контейнер для подробного описания каждой метки и каждого заголовка (является необязательным). 49 | В данном контейнере Вы можете разместить текст, ссылки, изображения и т.д. 50 | Самый первый тег `div` будет отображаться по умолчанию в исходном состоянии блока. 51 | - Не рекомендуется создавать или менять классы у элементов. 52 | При необходимости подключайтесь к уже созданным классам через CSS и кастомизируйте под собственный проект. 53 | 54 | ### В CSS необходимо настроить изображение и позицию меток: 55 | 56 | Вы можете записать следующие строки в своем файле CSS: 57 | 58 | - Указываем путь до главного изображения. 59 | Указывайте правильный ID (`#ar-inter`) для настраиваемого блока. 60 | 61 | ```css 62 | #ar-inter .ar-img { 63 | background: url('путь до изображения'); 64 | } 65 | ``` 66 | 67 | - Указываем увеличение и положение изображения при активации первой метки. 68 | При необходимости настраиваем положение для остальных меток (.ar-btn1, .ar-btn2, ..., .ar-btn49, .ar-btn50). 69 | 70 | ```css 71 | #ar-inter .ar-img.ar-btn1 { 72 | background-size: 460% !important; 73 | background-position-x: 25% !important; 74 | background-position-y: 20% !important; 75 | } 76 | ``` 77 | 78 | - Вы можете подставлять разные изображения на каждую метку. 79 | Для этого необходимо указать путь до изображения и его позицию для нужной метки. 80 | При необходимости настраиваем и для остальных меток (.ar-btn1, .ar-btn2, ..., .ar-btn49, .ar-btn50). 81 | 82 | ```css 83 | #ar-inter .ar-img.ar-btn1 { 84 | background-image: url('путь до изображения'); 85 | background-size: 100% !important; 86 | background-position-x: 0% !important; 87 | background-position-y: 0% !important; 88 | } 89 | ``` 90 | 91 | - Указываем положение метки на изображении. 92 | При необходимости настраиваем остальные метки (.ar-btn1, .ar-btn2, ..., .ar-btn49, .ar-btn50). 93 | 94 | ```css 95 | #ar-inter .ar-img .ar-btn1 { 96 | left: 27%; 97 | bottom: 70%; 98 | } 99 | ``` 100 | 101 | - Вы можете настроить всплывающую подсказку при наведении на метку. 102 | При необходимости настройте содержимое `content` для каждой метки (.ar-btn1, .ar-btn2, ..., .ar-btn49, .ar-btn50). 103 | 104 | ```css 105 | #ar-inter .ar-img .ar-btn1::before { 106 | content: 'Текст'; 107 | } 108 | ``` 109 | 110 | ### В JS необходимо вызвать функцию `arInter()`: 111 | 112 | - Вызов функции вы можете прописать в любом JS-файле, подключённом в HTML после `artag.js`. 113 | - Вызывать функцию `arInter()` необходимо для каждого блока. 114 | - Обязательные атрибуты функции `arInter()`: ID блока, ширина изображения, высота изображения. 115 | Указываем ширину и высоту исходного изображения, чтобы корректно работала адаптивность. 116 | 117 | ```javascript 118 | arInter('#ar-inter', 2560, 2005); 119 | ``` 120 | - Скрипт по умолчанию поддерживает до 50 меток на изображении. 121 | Если необходимо увеличить количество меток, то указываем это при вызове фунцкции arInter() 122 | Последним атрибутом передаем количесвто меток: 123 | 124 | ```javascript 125 | arInter('#ar-inter', 2560, 2005, 60); 126 | ``` --------------------------------------------------------------------------------