Jump to content
  • 0

Как через CSS нарисовать произвольную фигуру?


DraculaOleg
 Share

Question

Всем привет. Хочу на сайте создать блоки произвольной формы. Скрин ниже. Пробовал через SVG - да, получается, но мне это не подходит. Многовато будет весить страница, если таких фигур будет несколько. Так что этот вариант отпадает. Canvas - тоже не подходит. Ведь там JS, а он не у каждого пользователя включен (в браузере). Этот вариант тоже отпадает.
CSS - я не знаю, как с помощью CSS нарисовать такой блок. Нашёл лишь свойства, которые могут помочь: [сode]z-index, border-radius, border-width, transform, transform-origin, display, :after, :before, position. Но как - я не знаю. Пробовал просто "налепливать" фигуру на фигуру, чтобы получить нужную, но не получилось. Нет, получилось, но не так, как нужно. У многих "налепленных" фигур был фиксированный размер, а мне нужен резиновый. То есть в процентах. Помогите, пожалуйста. 

5280779.jpg
Link to comment
Share on other sites

6 answers to this question

Recommended Posts

  • 0
Ведь там JS, а он не у каждого пользователя включен (в браузере). Этот вариант тоже отпадает.
 

Зачем придумывать себе сложности?

Если нет джс - нет некоторой части функционала.

Такой элемент нарисовать на css будет затруднительно.

 

http://border-radius.vvy.me/

Вот манипуляции с бордер радиусом. Может чем-то поможет.

Link to comment
Share on other sites

  • 0
отключенный в бразуере js в наше время звучит глупо

не совсем. Например, FF28 (29 не знаю) не проходит тест на xss.  нужно ставить NoScript. А при этом можно заодно порезать и многое другое (если захотеть, а соблазн всегда есть). Не в тему js, но на данном форуме Ghostery банит аватарку анонимуса с gravatar.com Причем уловить это можно лишь при начальной загрузке. Поэтому можно видеть, например, у пользователя друзей  - 2, а не 7. Т.к. тематика безопасности развивается теперь немного быстрее, то логичны попытки пользователя повлиять на сайт т.к. владелец сайта исходно не гарантирует его защиты как таковой и защиты его личных данных. Конечно, вконтакте может себе позволить давать отлуп при выключенном js, а вот мелкий сайт - вопрос. Достаточно один раз обжечься...так не то, что js отключать. а вообще через lynx заходить и composer только на виртуалке в виртуалке запускать.

Link to comment
Share on other sites

  • 0

Как раз заинтересовался темой в заглавии и нашёл ссылочку:

 

http://www.internet-technologies.ru/articles/article_2075.html

 

Почитал, видно что в CSS можно сделать действительно много.

Особенно заинтересовала реализация звезды.

Решил её сделать, вот получилась такая хрень:

 

https://jsfiddle.net/Launder/vdspdkf3/

 

Начал думать что сделал не так, вроде сделал всё по уму, а грани не совпадают.

Стал разбираться подробно по исходной ссылке, там многое сделано с конкретными размерами, но откуда все эти цифры?

Можно, конечно, мой пример допилить:

 

https://jsfiddle.net/Launder/vdspdkf3/2/

 

Звезда кажется ровненькой, но такая ли она на самом деле?

И потом, интересно понять принцип...

Вроде, интуитивно ясно, что дело в средней точке. Все лепестки звезды должны находится в одинаковом положении по отношению к центру, и если провести из центральной точки отрезок до торца лепесков (вершин треугольника), то есть, до самой дальней точки каждого лепестка, то эти отрезки между вершиной и центром должны быть равны. Верно и обратное - отрезки проведённые из вершины каждого лепестка должны пересекаться в одной точке.

Так что же это за точка? Вроде она должна совпадать с центром треугольника, но что считать центром?...

Постепенно до меня дошло, чем и спешу с Вами поделиться!

Вот возьмём такой простой, исходный треугольник:

 

https://jsfiddle.net/Launder/vdspdkf3/5/

 

По умолчанию, поворот фигуры в CSS осуществляется через его центральную точку. Достаточно очевидно что она по горизонтальной координате совпадёт с верхним углом треугольника, а по вертикали будет равна половине его высоты. Если этой вертикалью разделить исходный треугольник, то мы получим два одинаковых треугольника, левый и правый. Поскольку вертикаль перпендикулярна нижней грани нашего большого треугольника, то рассечение вертикалью на двое делает получившиеся треугольники прямоугольными, поскольку их угол между нижней гранью и общей вертикалью - прямой. Отметим это, поскольку с прямоугольными треугольниками можно взаимодействовать с помощью тригонометрии.

Если из левой вершины левого треугольника, и из правой вершины правого направить луч, попадающий в нашу центральную точку, которая задана правилами CSS, то эти лучи делят вертикальный отрезок на две равные части, что означает, что такой луч - медиана этих углов.

А теперь давайте вспомним какая же должна быть та центральная точка, которую мы ищем. Из неё всё исходит, а значит "корень" каждого лепестка должен из неё как бы исходить, и луч соединяющий центральную точку с торцом лепестка(вершиной угла) должен идти ровно посередине этого лепестка. Верно и обратное, луч исходящий из вершины и идущий к центральной точке должен идти ровно посередине каждого лепестка (то есть быть его биссектрисой), а значит в каждой его точке он должен быть равноудалён от обоих боковых граней своего лепестка. Очевидно, что этот перпендикуляр будет немного наклонён, иначе наш луч, делящий боковой угол попалам, должен идти горизонтально, а боковые грани должны расходится вверх и вниз.

 

https://jsfiddle.net/Launder/vdspdkf3/6/

 

(представим себе идущий горизонтально из левой вершины луч, он будет делить угол попалам, при этом попалам будет делиться и треугольник, до тех пор, пока грани сохраняют первоначальное направление)

 

Но поскольку, в исходном треугольнике, одна грань (нижняя) у нас лежит на горизонтальной плоскости, луч уже направлен под углом, верхняя грань под ещё большим (вдвое) углом. Перпендикуляр же к лучу окажется наклонён!

А теперь давайте посмотрим на нашу медиану. Её отрезки равны, но поскольку она не наклонена, то соединяет она более дальнюю точку верхней грани и более ближнюю нижней. Если взять точку на верхней грани и провести отрезок перпендикулярный нашему лучу, то точка на нижней грани будет дальше центра. Если взять центральную точку на нижней грани и провести перпендикуляр к лучу, то точка на верхней грани будет ближе центра.

Если же взять пересечение вертикали треугольника и луча исходящего из боковой вершины (и делящего боковой угол попалам), то центральная вертикаль находится как бы под углом к перпендикуляру луча, соответственно, одна часть  вертикали (верхняя) будет как бы тянутся к более удалённой точке на боковой грани, а другая её часть(нижняя) к более ближней точке на боковой грани. Поэтому эти отрезки будут не равны! А значит срединная точка, в любом случае не будет совпадать с серединой вертикали (иначе отрезки были бы равны). Как же нам выяснить её местоположение?

Вспомним, что вертикаль делит большой треугольник на два маленьких. Наш биссектриса отсекает ещё половину бокового угла и делает треугольник более низким. Итак у нас есть маленький прямоугольный треугольник. Нам нужно найти противолежащую биссектрисе сторону, зная размер его прилежащей стороны - 100px. Для этого нам нужно найти угол получившегося треугольника, между нижней гранью и биссектрисой, а далее воспользоваться тригонометрией. Понятно, что этот угол будет вдвое меньше исходного. Размер исходного нам не известен. У нас есть только размеры его прилежащего и противолежащих сторон. Прилежащая - 100px, противолежащая 70px. Если б наша биссектриса и медиана совпадали (как, например, у большого треугольника вертикаль), то мы могли бы сразу сказать что противолежащая сторона нашего маленького треугольника равно половине от большого - 50px, но поскольку биссектриса и медиана боковой стороны у наших маленьких треугольников не совпадают, то это не так. Поэтому нам придётся сначала найти угол большого треугольника, потом его разделить попалам, а потом уже искать размер стороны, исходя из полученного значения.

Итак - прилежащая - 100px, противолежащая - 70px. Делим противолежащую на прилежащую - получаем 0.7. Итак тангенс равен 0.7. Арктангенс получается равен 0.61 радиан. Поскольку 180 градусов соответствует пи (3.1415) радиан, а нам нужно выяснить сколько градусов составляет 0.61 радиан - делаем пропорцию. Получаем 35 градусов. Интересно что 36 градусов - десятая часть полного круга, а поскольку наша пентограмма делит круг на 5 частей, то 36 градусов это половина от этой части. Получается, если бы наши лучи звезды не вершинами бы замыкали лепестки, а, наоборот, вершинами исходили бы из центральной точки, то грани бы заняли бы в сумме как раз половину круга.

В нашем неидеальном случае соотношение получается такое, что угол лепестка - 35 градусов. Едем дальше - делим его попалам, получае 17,5 градусов, округляем до 18 градусов (кстати как раз половина от 36, о которой мы говорили). Теперь, зная угол и прилежащую сторону в сто пикселей, нам нужно найти противолежащую сторону. Вычисляем тангенс 18 градусов, это чуть меньше 0.325, округляем до 0.32, и умножаем прилежащую сторону на тангенс: 0.32 x 100px получаем 32 пикселя высоты.

И вот она искомая точка!

Осталось за малым, ВСЕМ нашим треугольникам, образующим звезду поставить это значение центральной точки (ну а дальше уже их двигать на нужный угол). Поскольку плоскость идёт у нас от верхнего левого угла и ось Y направлена вниз, то координаты вычисляются так: X - 50%, Y вычислим - исходная высота равна 70px, от 70 - 32 = 38 пикселей от верхней точки.

Итого, для КАЖДОГО треугольника ставим: transform-origin: 50% 38px (можно 100px 38 px); а дальше вращаем их как хотим. А хотим мы разнести их на 72 градуса (360/5) друг от друга. Это, в общем-то не сложно и реализация видна из кода.

Чтобы центр круга совпал с центральной точкой нашего треугольника, нужно сделать следующее. Во-первых нужно учесть, что те относительные размеры, которые мы давали при трансформации (100% - размер фигуры), не совпадают с размерами абсолютного позиционирования. Фигура начинается с точки left: 0 top: 0 (помним что <li> находит в контейнере <ul>, поэтому эта точка не совпадает с левой верхней частью экрана, а совпадает с верхней левой частью контейнера <ul>), после того как я компенсирую размеры самой фигуры transform: translate(-50%, -50%) точка left: 0 top: 0 совпадает с центром фигуры. Поскольку мы знаем, что центр треугольника считается CSS как центр его вертикальной медианы, а нам нужен центр биссектрисы, то мы считаем центр медианы - 70 / 2 = 35px, центр биссектрисы мы уже посчитали - 38px вниз, поэтому нам круг нужно сдвинуть на три пикселя вниз, тогда его центр совпадёт с центром треугольников.

 

https://jsfiddle.net/Launder/vdspdkf3/4/

 

Вот и всё. Алкогоритм, вроде, более-менее ясен =)

Edited by Launder
  • Like 1
Link to comment
Share on other sites

  • 0

На счёт фигуры на чистом ксс - если фигура сложная, лучше уж попробовать SVG.

Делал как-то шестиугольник с фоновой картинкой на ксс - много времени потратил(( а если ещё и с границей.....в общем, иногда лучше искать альтернативу.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Answer this question...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

  • Similar Content

    • By zeiger2
      Здравствуйте! У меня стоит задача, что при наведении на блок li строка должна поменять цвет, в том числе и картинка. Я меняю картинку с помощью 
      background-image: none;     background: url(../img/check_icon_red.png) left no-repeat;   Но теперь картинка позицианируется не там где должна, её можно поставить на место только вручную, через -100px. Нужно поставить ровно туда, где она была. Должна быть в одном ряду с другими
    • By Mix9
      есть див с 5 img, при уменьшении экрана див выходит за него. Я добавил overflow: auto для этого div в надежде на то, что я смогу прокручивать фотки с помощью скроллбара, однако даже с ним почему-то я не вижу часть фоток которые вышли за границу. Что с этим можно сделать? класс video повторяется 5 раз, я тут оставил только 1 
      .content{ width: 90%; background-color: #333; } .video{ margin: 0px 4px 0px 4px; width: 310; display: flex; flex-direction:column; } .video_button_text{ margin-top: 10px; display: flex; flex-direction: row; font-size: 20px; color: white; } .video_text_div{ display: inline-block; width: 250px; } .video_text{ text-align: justify-all; margin: 0px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } .slidan_videos{ margin: 0px 10px 0px 20px; overflow: auto; width: auto; margin-bottom: 50px; display: flex; flex-direction: row; justify-content: space-around; } <div class="content"> <div class = slidan_videos> <div class = video> <div> <a href = 'ссылка'><img class="img" src=""картинка"></a> </div> <div class = video_button_text> <div class = avatarka_div> <a href="ссылка" target="_blank"><img class = avatarka src="картинка"></a> </div> <div class = video_text_div> <p class = video_text><a href="ссылка">текст</a></p> </div> </div> </div>
    • By Kaido
      Использую готовый плагин для модальных окон(от MaxGraph). Проблема в том, что когда у меня открыто два модальных окна, для примера Форма + Политика конфендициальности, и мне нужно закрыть политику вместе с ней закрывается и другое модальное окно. В JS я не сильно разбираюсь(собственно из за этого и использую готовый плагин), можете помочь кто работал с этим плагином? Я примерно понимаю как он работает, но реализовать чтобы закрывалось только одно не получается.
       
        <div class="content"> <button class="modal-btn" data-path="first" data-animation="fadeInUp" data-speed="1500">Открыть окно 1</button> </div> <div class="modal"> <div class="modal__wrapp" data-target="first"> <div class="modal__content"> <button class="modal__close">Закрыть</button> модальное окно <button data-path="policy">Политика</button> </div> </div> <div class="modal__wrapp" data-target="policy"> <div class="modal__content"> <button class="modal__close">Закрыть</button> политика </div> </div> </div> .modal { --transition-time: 0.3s; position: fixed; left: 0; top: 0; right: 0; bottom: 0; z-index: 1000; cursor: pointer; overflow-y: auto; overflow-x: hidden; text-align: center; opacity: 0; visibility: hidden; transition: opacity var(--transition-time), visibility var(--transition-time); } .modal__wrapp { display: none; cursor: default; width: fit-content; height: fit-content; } .modal__content{ position: absolute; left: 500px; width: 500px; height: 500px; display: flex; color: white; flex-direction: column; text-align: left; background-color: #000; } .modal__content button{ width: 200px; height: 50px; margin: 50px 0; } .modal.is-open { opacity: 1; visibility: visible; transition: opacity var(--transition-time), visibility var(--transition-time); } .modal__wrapp.modal-open { display: flex; } .disable-scroll { position: relative; overflow: hidden; height: 100vh; position: fixed; left: 0; top: 0; width: 100%; } .fade { opacity: 0; transition: opacity var(--transition-time); } .fade.animate-open { opacity: 1; transition: opacity var(--transition-time); } .fadeInUp { opacity: 0; transform: translateY(vw(-100)); transition: opacity var(--transition-time), transform var(--transition-time); } .fadeInUp.animate-open { opacity: 1; transform: translateY(0); transition: opacity var(--transition-time), transform var(--transition-time); } .modal__wrapp[data-target="policy"] .modal__content{ left: 1050px; background-color: #000; opacity: .5; } class Modal { constructor(options) { let defaultOptions = { isOpen: () => {}, isClose: () => {}, } this.options = Object.assign(defaultOptions, options); this.modal = document.querySelector('.modal'); this.speed = false; this.animation = false; this.isOpen = false; this.modalContainer = false; this.previousActiveElement = false; this.fixBlocks = document.querySelectorAll('.fix-block'); this.focusElements = [ 'a[href]', 'input', 'button', 'select', 'textarea', '[tabindex]' ]; this.events(); } events() { if (this.modal) { document.addEventListener('click', function(e){ const clickedElement = e.target.closest('[data-path]'); if (clickedElement) { let target = clickedElement.dataset.path; let animation = clickedElement.dataset.animation; if (clickedElement.classList.contains('modal-close')) { this.close(); } let speed = clickedElement.dataset.speed; this.animation = animation ? animation : 'fade'; this.speed = speed ? parseInt(speed) : 300; this.modalContainer = document.querySelector(`[data-target="${target}"]`); this.open(); return; } if (e.target.closest('.modal__close')) { this.close(); return; } }.bind(this)); window.addEventListener('keydown', function(e) { if (e.keyCode == 27) { if (this.isOpen) { this.close(); } } if (e.keyCode == 9 && this.isOpen) { this.focusCatch(e); return; } }.bind(this)); this.modal.addEventListener('click', function(e) { if (!e.target.classList.contains('modal__wrapp') && !e.target.closest('.modal__wrapp') && this.isOpen) { this.close(); } }.bind(this)); } } open() { this.previousActiveElement = document.activeElement; this.modal.style.setProperty('--transition-time', `${this.speed / 1000}s`); this.modal.classList.add('is-open'); this.disableScroll(); this.modalContainer.classList.add('modal-open'); this.modalContainer.classList.add(this.animation); setTimeout(() => { this.options.isOpen(this); this.modalContainer.classList.add('animate-open'); this.isOpen = true; this.focusTrap(); }, this.speed); } close() { if (this.modalContainer) { this.modalContainer.classList.remove('animate-open'); this.modalContainer.classList.remove(this.animation); this.modal.classList.remove('is-open'); this.modalContainer.classList.remove('modal-open'); this.enableScroll(); this.options.isClose(this); this.isOpen = false; this.focusTrap(); } } focusCatch(e) { const focusable = this.modalContainer.querySelectorAll(this.focusElements); const focusArray = Array.prototype.slice.call(focusable); const focusedIndex = focusArray.indexOf(document.activeElement); if (e.shiftKey && focusedIndex === 0) { focusArray[focusArray.length - 1].focus(); e.preventDefault(); } if (!e.shiftKey && focusedIndex === focusArray.length - 1) { focusArray[0].focus(); e.preventDefault(); } } focusTrap() { const focusable = this.modalContainer.querySelectorAll(this.focusElements); if (this.isOpen) { focusable[0].focus(); } else { this.previousActiveElement.focus(); } } disableScroll() { let pagePosition = window.scrollY; this.lockPadding(); document.body.classList.add('disable-scroll'); document.body.dataset.position = pagePosition; document.body.style.top = -pagePosition + 'px'; } enableScroll() { let pagePosition = parseInt(document.body.dataset.position, 10); this.unlockPadding(); document.body.style.top = 'auto'; document.body.classList.remove('disable-scroll'); window.scroll({ top: pagePosition, left: 0 }); document.body.removeAttribute('data-position'); } lockPadding() { let paddingOffset = window.innerWidth - document.body.offsetWidth + 'px'; this.fixBlocks.forEach((el) => { el.style.paddingRight = paddingOffset; }); document.body.style.paddingRight = paddingOffset; } unlockPadding() { this.fixBlocks.forEach((el) => { el.style.paddingRight = '0px'; }); document.body.style.paddingRight = '0px'; } } const modal = new Modal({ isOpen: (modal) => { console.log(modal); console.log('opened'); }, isClose: () => { console.log('closed'); }, });  
    • By stasN1
      Мне нужен такой результат :

      Не понимаю почему background не применяется вокруг иконок:

       
      Сам код:
      Html:
      css:

      Html:
      <section class="finish"> <div class="container"> <h2 class="finish_header">По окончании обучения Вы сможете!</h2> <div class="finish_divider"></div> <div class="finish_wrapper"> <finish_item> <div class="finish_round"> <img src="/icons/finish/1 (1).png" alt="" class="finish_icon"> </div> <div class="finish_descr">Создать свой сайт или блог</div> </finish_item> <finish_item> <div class="finish_round"> <img src="/icons/finish/2.png" alt="" class="finish_icon"> </div> <div class="finish_descr">Создать свой сайт или блог</div> </finish_item> <finish_item> <div class="finish_round"> <img src="/icons/finish/3.png" alt="" class="finish_icon"> </div> <div class="finish_descr">Обеспечить ему медленный, но верный рост в ТОП</div> </finish_item> <finish_item> <div class="finish_round"> <img src="/icons/finish/4.png" alt="" class="finish_icon"> </div> <div class="finish_descr">Достигнуть стабильного прироста посетителей</div> </finish_item> <finish_item> <div class="finish_round"> <img src="/icons/finish/5.png" alt="" class="finish_icon"> </div> <div class="finish_descr">Достигнуть стабильного прироста посетителей</div> </finish_item> </div> </div> </section>  
      Css:
      .finish .finish_wrapper { margin-top: 41px; display: flex; justify-content: space-between; } .finish .finish_wrapper .finish_item { width: 204px; } .finish .finish_wrapper .finish_item .finish_round { width: 115px; height: 115px; background-color: #b4e2ff; border-radius: 8px; } .finish_descr { font-family: Roboto; font-size: 17px; line-height: 20px; font-weight: 300; color: #efefef; Спасибо!
       

      Уже решил, спасибо!
      У меня CSS селектор вида:
      .finish .finish_wrapper .finish_item .finish_round

      А должен быть:
      .finish .finish_wrapper finish_item .finish_round
      Т.е. без точки перед finish_item, так как это не класс, а элемент
    • By Dos1er
      Приветствую! 
      Подскажите, плиз, как сделать чекбокс как на скрине? 
      Я новичок в верстке и что-то не могу разобраться в кастомизации данной штуки

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue. See more about our Guidelines and Privacy Policy