Warning: Use of undefined constant ddsg_language - assumed 'ddsg_language' (this will throw an Error in a future version of PHP) in /var/www/applefavorite/data/www/applefavorite.ru/wp-content/plugins/sitemap-generator/sitemap-generator.php on line 45
Твик MusicMod: всё о настройке интерфейса Apple Music

Твик MusicMod

Настройка

Позже мы покажем ту же обложку альбома уменьшенной в мини-плеере… и сделаем так, что весь экран «Исполняется» исчезнет. Вот почему мы должны изъять слой обложки альбома из его родительского слоя и поместить его прямо в компонент прокрутки.

Это легко сделать одной строкой:

$.Album_Cover.parent = scroll_now_playing.content

Теперь всё ещё находится в компоненте прокрутки, но независимо, как родственный элемент экрана «Исполняется». (И нам даже не пришлось исправлять его положение.)

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

$.Album_Cover_shadow.visible = no

Создание состояний «воспроизводится» и «на паузе»

Теперь мы можем установить значения для состояний.

Когда музыка воспроизводится, обложка альбома должна выглядеть так:

Так должна выглядеть обложка альбома при проигрывании музыки

  • она показана в полном размере в 311 х 311 пунктов с масштабом () ;
  • цвет тени на 40% чёрный — ;
  • тень проецируется вниз — пунктов …
  • … и наружу во всех направлениях — (распространение тени) пунктов;
  • (нет горизонтальной тени, 😉
  • размытость тени также высока — пунктов размытия Гаусса ();

А когда музыка приостановлена, обложка должна выглядеть так:

А так должна выглядеть обложка альбома, когда музыка приостановлена

  • 249 х 249 — составит ;
  • тень очень светлая: только 10% чёрного цвета — ;
  • вертикальная тень, — пунктов;
  • нет ;
  • — пунктов;

(Тень на самом деле окажется на 20% меньше из-за изменения масштаба.)

Для простоты мы будем называть наши состояния и . Оба их можно определить одновременно:

$.Album_Cover.states =    playing:        scale: 1        shadowType: "outer"        shadowColor: "rgba(0,0,0,0.4)"        shadowY: 20        shadowSpread: 10        shadowBlur: 50        frame: $.Album_Cover.frame        animationOptions:            time: 0.8            curve: Spring(damping: 0.60)    paused:        scale: 0.8        shadowType: "outer"        shadowColor: "rgba(0,0,0,0.1)"        shadowY: 19        shadowSpread: 0        shadowBlur: 37        frame: $.Album_Cover.frame        animationOptions:            time: 0.5

Я также включил первоначальное значение свойства слоя в каждое состояние для того, чтобы позже можно было добавить третье состояние мини-плеера, в котором мы изменим положение обложки.

Также я включил (параметры анимации):

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

Для проверки эффектов анимации можно выполнить между ними, нажав на обложку альбома:

$.Album_Cover.onTap ->    this.stateCycle "paused", "playing"

(Включив в код имена состояний, сделаем так, что состояние будет проигнорировано.)

Проверка анимации состояний обложки альбома

Выглядит как надо.

Можно удалить событие , потому что анимация состояний будет запускаться вместе с включением и остановкой музыки.

С помощью можно переключить слой в определенное состояние без анимации. Используем эту функцию, чтобы сделать начальным состоянием.

$.Album_Cover.stateSwitch "paused"

Размытие фона

Теперь, когда видна прозрачность мини-плеера, можно заметить, что чего-то не хватает — размытия фона. Всё, что находится под мини-плеером, должно быть размыто.

Вернитесь к Design, выберите мини-плеер, и добавьте Blur со значением , а затем измените тип размытия с Layer на Background.

Вот результат:

просмотреть — открыть во Framer

Ах да, теперь, когда мы перешли в мини-плеер, можно «щёлкнуть выключателем»:

# Мини-плеер активенminiPlayerActive = yes

21. Переход от мини-плеера обратно к экрану «Исполняется»

Теперь желательно вернуть предыдущее состояние. При нажатии на мини-плеер он должен превратиться в экран «Исполняется»

Мы прослушаем событие на слое заднего плана мини-плеера.

Mini_Player_Background.onTap ->

Почему задний план? Потому что таким образом мы можем продолжать использовать кнопки воспроизведения и паузы на мини-плеере, не запуская этот переход.

Сначала в обработчике события сделаем экран «Исполняется» видимым:

# Показывать экран «Исполняется»,# чтобы ему не пришлось появляться в процессе$.Now_Playing.opacity = 1

В любом случае, он находится под мини-плеером, и, сдвигая его, мы не хотим анимировать его непрозрачность.

Добавление таймера оставшегося времени

Также есть таймер, отсчитывающий оставшееся время.

Он имеет те же свойства текста, что и таймер , поэтому можно просто скопировать это …

timeRemaining = timePlayed.copy()

… а затем изменить несколько свойств, чтобы переместить его вправо:

  • Его правый край, , должен быть выровнен с той же стороной .
  • Его текст должен быть выровнен по правому краю.
  • И, чтобы работало выравнивание текста по правому краю, ширина его должна быть фиксированной.
timeRemaining.props =    textAlign: Align.right    width: 60    maxX: progressBar.maxX    parent: $.Now_Playing

Передаём его аудиоплееру с .

audio.showTimeLeft timeRemaining

Можно заметить, что теперь всё размещено точно поверх оригинального из Sketch, поэтому его можно скрыть:

$.Progress_bar.visible = no

Кстати, теперь можно видеть, когда музыка загрузилась. Когда таймер оставшегося времени покажет правильную продолжительность, , это будет означать, что файл готов. Если это занимает слишком много времени, можно загрузить файл и сохранить его в папке проекта (лучше всего в новой папке «sounds»). Конечно, в этом случае необходимо изменить URL на локальный.

Ограничение перемещения прокрутки

Есть, однако, одна мелочь, которую нужно исправить. Легко заметить, что при прокрутке влево или вправо можно нечаянно крутануть вверх или вниз. В оригинальном приложении такого нет.

Когда вы начинаете прокрутку в определенном направлении, прокрутка в другом направлении должна блокироваться. Для этого нам нужно включить для обоих ScrollComponents. Они должны выглядеть так:

# Компонент прокрутки для всего артбордаscroll_for_you = ScrollComponent.wrap $.For_youscroll_for_you.props =    scrollHorizontal: no    contentInset:        bottom: $.Tabs.height + 40directionLock: yes# Компонент прокрутки для раздела недавно воспроизведённогоrecentlyPlayed = ScrollComponent.wrap $.Recently_Played_albumsrecentlyPlayed.props =    scrollVertical: no    contentInset:        right: 20directionLock: yes

Второй набор анимаций

Второй набор начинается в то же время, но эти анимации выполняются медленнее: секунды. Как и в первом наборе, они используют кривую , установленную по умолчанию.

# -- Второй набор анимаций: полсекунды -- #secondSetDuration = 0.5

Наиболее заметная анимация — движущийся обратно вверх экран «Исполняется»:

# Анимируем компонент прокрутки вверхscroll_now_playing.animate    y: 33    borderRadius:        topLeft: 10        topRight: 10    options:        time: secondSetDuration

(Заодно восстанавливаем радиус закругления на верхних углах.)

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

Как известно, объект в аудиоплеере предоставляет доступ к своему элементу HTML5-аудио. Когда музыка не воспроизводится, одно из свойств этого элемента, , будет иметь значение ‘true’ (верно).

if audio.player.paused    $.Album_Cover.animate "paused",        time: secondSetDurationelse    $.Album_Cover.animate "playing",        time: secondSetDuration        curve: Bezier.ease

Указывая параметр , перезаписываем длительность, заданную при создании состояний.

Кроме того, анимационная кривая в не должна быть «пружинистой», поэтому замещаем её кривой .

просмотреть — открыть во Framer

Что остаётся? Всё, что происходит на заднем плане под экраном «Исполняется»:

  • прозрачный серый оверлей появляется снова;
  • экран на заднем плане снова становится картой;
  • строка состояния становится белой.

Серый оверлей:

# Показывать прозрачный серый оверлейoverlay.animate    opacity: 1    options:        time: secondSetDuration

Разбираемся с экранами «Library» и «For You»:

# Сжать и переместить экраны на заднем планеscroll_library.animate    scaleX: 0.93    y: 20    borderRadius: 10    options:        time: secondSetDurationscroll_for_you.animate    scaleX: 0.93    y: 20    borderRadius: 10    options:        time: secondSetDuration

(Опять же, только один из них будет виден на данный момент.)

Строка состояния:

# Сделать строку состояния белой$.Status_Bar.animate    invert: 100    options:        time: secondSetDuration

Всё настроено. Теперь надо отключить мини-плеер, чтобы он не запускался непреднамеренно, когда пользователь скролит экран вниз …

Mini_Player.visible = no

… и указываем, что мини-плеер неактивен.

miniPlayerActive = no

просмотреть — открыть во Framer

Делаем кнопки воспроизведения и паузы кликабельными

Придадим этим кнопкам способность проигрывать и приостанавливать музыку, используя функции и (из HTML5 аудио) в объекте аудиоплеера:

Mini_Button_Play.onTap ->    audio.player.play()Mini_Button_Pause.onTap ->    audio.player.pause()

Можно было бы использовать те же обработчики событий , чтобы кнопки отображались и исчезали (для переключения между кнопками «Play» и «Pause»).

Но мы уже «слушали» некоторые события ‘a и знаем, когда воспроизведение музыки началось или остановилось. Если помните, они используются для увеличения и уменьшения обложки альбома.

Вернёмся к фолду и добавим следующие строки:

# Когда музыка начала воспроизводитьсяaudio.player.onplaying = ->    $.Album_Cover.animate "playing"    # Показать и скрыть маленькие кнопки    Mini_Button_Play.visible = no    Mini_Button_Pause.visible = yes# Когда музыка приостановленаaudio.player.onpause = ->    $.Album_Cover.animate "paused"    # Показать и скрыть маленькие кнопки    Mini_Button_Play.visible = yes    Mini_Button_Pause.visible = no

Таким образом, при нажатии на большие кнопки на экране «Исполняется» маленькие кнопки тоже изменятся.

Чтобы сделать возможным обратное действие (большие кнопки должны меняться при нажатии на маленькие), добавим похожие строки для и .

# Когда музыка начала воспроизводитьсяaudio.player.onplaying = ->    $.Album_Cover.animate "playing"    # Показать и скрыть маленькие кнопки    Mini_Button_Play.visible = no    Mini_Button_Pause.visible = yes # … а также большие кнопки    $.Button_Play.visible = no    $.Button_Pause.visible = yes# Когда музыка приостановленаaudio.player.onpause = ->    $.Album_Cover.animate "paused"    # Показать и скрыть маленькие кнопки    Mini_Button_Play.visible = yes    Mini_Button_Pause.visible = no # … а также большие кнопки    $.Button_Play.visible = yes    $.Button_Pause.visible = no

Теперь все кнопки будут меняться в одно и то же время, независимо от того, какая кнопка «Play» или «Pause» (большая или малая) была нажата.

просмотреть — открыть во Framer

19. Маленькая версия обложки альбома в мини-плеере

Сделаем теперь дополнительное состояние для обложки альбома, в котором она будет маленькой и будет располагаться в мини-плеере с корректными параметрами тени.

Но, во-первых, как можно заметить при воспроизведении музыки, обложка альбома находится за мини-плеером. Это легко исправить с :

$.Album_Cover.placeBefore Mini_Player

Для этого нового состояния, , мы можем скопировать свойства , той крошечной обложки альбома, которую мы создали в Design. Воспользуемся её , , и …

$.Album_Cover.states.mini =    frame: Mini_Album_Cover.frame    shadowColor: Mini_Album_Cover.shadowColor    shadowY: Mini_Album_Cover.shadowY    shadowBlur: Mini_Album_Cover.shadowBlur    shadowSpread: Mini_Album_Cover.shadowSpread    scale: Mini_Album_Cover.scale

… а также установим и , потому что эти свойства были изменены другими состояниями. ( имеет значения по умолчанию: = , а = .)

На самом деле не должна быть видна; просто нужно было скопировать её свойства. Теперь мы можем скрыть её:

Mini_Album_Cover.visible = no

Для проверки можно активировать анимацию в новое состояние нажатием на обложку альбома:

$.Album_Cover.onTap ->    $.Album_Cover.animate "mini"

просмотреть — открыть во Framer

20. Переход с экрана «Исполняется» в мини-плеер

Всё выглядит как надо. На данный момент мини-плеер можно скрыть.

Mini_Player.opacity = 0

Используем (непрозрачность), потому что хотим его анимировать.

Но мы не хотим, чтобы он был нажат случайно, когда пользователь прокручивает экран «Исполняется» вниз, поэтому отключим также его .

Mini_Player.visible = no

Позже потребуется знать, используется ли мини-плеер (вы поймёте, почему). Создадим для этого переменную , которая в настоящий момент всё равно будет иметь значение ‘’.

miniPlayerActive = no

Анимация между состояниями, когда музыка включается и останавливается

Для запуска этих анимаций можно было бы использовать -события на кнопках воспроизведения и паузы, как например …

$.Button_Play.onTap ->    $.Album_Cover.animate "playing"

… но позднее у нас будет ещё две кнопки: те, что в мини-плеере.

Так что сделаем это по-другому. Будем отслеживать события и аудиоплеера.

В аудиоплеере есть объект , представляющий собой ни что иное, как аудио-элемент HTML5, фактически воспроизводящий музыку. И, судя по всему, можно добавить к нему функции, которые будут выполняться при возникновении события. Делаем это, создавая функцию в с перед именем события.

Таким образом и становятся и .

# Когда музыка начала воспроизводитьсяaudio.player.onplaying = ->    $.Album_Cover.animate "playing"# Когда музыка приостановленаaudio.player.onpause = ->    $.Album_Cover.animate "paused"

Добавление текстового слоя

Давайте, сначала сделаем (текстовый слой) с правильным размером шрифта, весом, положением и цветом. А затем сделаем его текст динамическим с помощью .

Наш текстовый слой:

today = new TextLayer    text: "SATURDAY, JUNE 17"    fontSize: 13.5    color: "red"    parent: $.Header_For_You    x: $.Today_s_date.x    y: $.Today_s_date.y

Нет необходимости устанавливать его (семейство шрифтов), потому что на вашем Mac, как и на iOS шрифтом по умолчанию будет Сан-Франциско. Eго (размер шрифта), составляет, по-видимому, пунктов. (Это будет 27 пикселей.)

Я использовал (красный) как контрастный (и временный) цвет текста, чтобы легче было найти правильную позицию.

Существующая дата находится в отдельном слое, , и его родитель—. Предоставив нашему текстовому слою тот же самый , можно повторно использовать позицию .

Как видите, нашему текстовому слою необходимо немного подвинуться вверх. Уменьшив его –позицию на 7 пикселей, расположим его в правильном месте.

y: $.Today_s_date.y - 3.5

Добавление индикатора воспроизведения

Для индикатора воспроизведения и ползунка громкости модуль Framer Audio использует слайдеры.

(компоненту слайдера) можно придать любой желаемый вид (или создать его в Design), а затем передать его аудиоплееру.

Попробуйте этот слайдер:

progressBar = new SliderComponent    width: 311    height: 3    backgroundColor: "#DBDBDB"    knobSize: 7    x: Align.center    y: 363    parent: $.Now_Playing

По ширине он такой же, как обложка альбома, пунктов, и он тонкий, всего пункта в высоту. Цвет слайдера светло-серый .

Размер ручки, , также довольно мал, всего пунктов.

Сделав его родительским элементом, , поместим его внутри экрана «Исполняется». Используем , чтобы центрировать его по горизонтали и размещаем его на уровне пунктов от верхнего края.

Левая часть компонента слайдера — это его заполнение, . Это дочерний слой, поэтому его цвет придётся установить отдельно:

progressBar.fill.backgroundColor = "#8C8C91"

То же самое справедливо и для , который получит тот же цвет, что и заполнение:

progressBar.knob.props =     backgroundColor: progressBar.fill.backgroundColor    shadowColor: null

(Избавляемся от тени по умолчанию, устанавливая равным .)

Теперь, чтобы активировать слайдер, передайте его функции аудиоплеера .

audio.showProgress progressBar

Функция, которая выводит сегодняшнюю дату

Теперь , которая выдает текущую дату в виде текстовой строки:

todaysDate = ->    days =     months =     now = new Date()    dayOfTheWeekNumber = now.getDay()      # = число от 0 до 6    monthNumber = now.getMonth()           # = число от 0 до 11    theDateAsText = days + ", " + months + " " + now.getDate()    return theDateAsText

Я объясню это по строкам.

Первая строка создает функцию .

todaysDate = ->

Стрелка означает: «Это функция, и следующие строки должны запускаться при её вызове».

Первая строка в нашей новой функции просто создает (массив), , который содержит названия дней недели, …

days =

… а вторая строка делает то же самое для названий месяцев.

months =

Затем мы создаём , объект даты JavaScript, используя .

now = new Date()

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

Объект имеет множество встроенных функций, мы будем использовать три из них:

  • , чтобы получить текущий день недели. Она возвращает число от до . Думаете, что первым днем недели должен быть понедельник (или суббота)? … Но в этом случае означает воскресенье.
  • , чтобы получить номер текущего месяца. Здесь никаких проблем: первым всегда будет январь.
  • , чтобы получить день в месяце. Эта функция не использует нумерацию от нуля (zero-based numbering), как предыдущие, поэтому числа просто начинаются с .

Сначала мы получаем числа текущего дня недели и месяца и сохраняем их в и .

dayOfTheWeekNumber = now.getDay()         # = число от 0 до 6monthNumber        = now.getMonth()       # = число от 0 до 11

Теперь мы можем собрать всё это вместе и .

theDateAsText = days + ", " + months + " " + now.getDate()

Первая часть, , выбирает правильный день недели из массива, созданного ранее, а вторая часть, , делает то же самое с названием месяца.

Мы объединяем их (с запятой и пробелом, , между ними) и ставим день месяца в конец с помощью .

И тогда последняя строка нашей функции возвращает эту текстовую строку с .

return theDateAsText

Когда вы вызовете функцию и распечатаете () её, например, так …

print todaysDate()

… в Консоли появится сегодняшняя дата.

Ссылка на основную публикацию