Введение
Самая популярная новость на фронтенде в эти дни — это не что иное, как выпуск vue3.0, хоть это и не официальная версия, но тоже заставляет людей хотеть узнать.
Во второй половине дня ничего не было, поэтому я пропустил документ. Но лучший способ научиться — это применить его.
Оглядевшись, я случайно увидел календарь win10, упс, да, это ты.
Эффект следующий:
Простой онлайн-прототип
ссылка на полный превью
Соберите проект vue3.0
Рабочие хотят что-то делать, сначала нужно запустить эшафот, хорошая поддержка @ Vue / CLI, проект очень простой.
vue create win10-calendarcd win10-calendar && vue add vue-next
анализ спроса
Требования очень четкие, скопируйте.
Перед копированием изучите объект копирования.
После аккуратной работы можно обнаружить, что в заголовке календаря есть три рабочих области.
Нажмите на часть года/месяца, чтобы переключить представление, которое представляет собой таблицу календаря, таблицу месяца, таблицу года и прогрессивные слои.
Стрелки вверх и вниз имеют разные функции в разных представлениях: таблица календаря управляет месяцем, таблица месяцев управляет годом, а таблица годов управляет N годами одновременно.
Когда мышь подвешена в форме, есть эффект прожектора, очень интересно.
Переключение таблиц также имеет анимационные эффекты отступа и расширения.
На этом этапе организация кода в основном определена.
Запись компонента, заголовок компонента, таблица календаря, таблица месяца, таблица года 5 компонентов.
Эффект прожектора также выделен как отдельный компонент для простоты абстрагирования. Всего 6 компонентов.
Кажется, что эти три таблицы показывают разные вещи, но на самом деле они связаны друг с другом: все они показывают разные измерения одной и той же даты.
Руководитель отвечает за изменение и редактирование этой даты, а также за переключение между различными представлениями.
Запись компонента, естественно, отвечает за интеграцию этих подкомпонентов, и состояние подкомпонента также необходимо повысить до его уровня.
Давайте еще раз посмотрим на прожектор, в css нет свойства прожектора, которое может быть похоже наbox-shadowТак же удобно, плюс просто выделение области.
Но магия css — это головоломка, над этим нужно подумать.
Предположим, мы делаем полностью черную маску, выкапываем дырку посередине и, когда мышь движется, заставляем центр маски следовать за мышью, разве это не прожектор?
Что касается текста, когда он не подвешен, текст тоже должен быть виден. Когда он подвешен, текст и граница сетки видны одновременно. Это тоже очень просто. Расположите текст, увеличьте z-индекс , и пусть они [поверхности].
написать компоненты
Говорят, что vue3 поддерживает большинство функций vue2.x. Отдельный файл Vue по-прежнему поддерживается.
новыйCalendar/index.vueкомпонент и, кстати, добавьте подкомпоненты. Структура файла следующая
Calendar
index.vue
children
CalendarHead.vue
DatPanel.vue
MonhPanel.vue
YeaPanel.vue
Mask.js
В скриптовой части большинство атрибутов можно удалить. Добавьте функцию настройки.
В vue3 функция настройки вызывается между beforeCreate и created. Он может возвращать функцию рендеринга или объект. Поля, содержащиеся в объекте, затем можно использовать в шаблоне.
Начальная стадия индекса
Сначала добавьте некоторое глобальное состояние в index.vue, например, объекты даты.
Эти состояния должны быть переданы в подсборку, подсборка также должна иметь возможность изменять эти состояния. vue2.x общее использование реквизита/события, если честно, немного громоздко. Если vuex, они кажутся слишком громоздкими.
Vue3 предоставляет механизм предоставления Provide/INJECT, хотя Vue2.x тоже доступен, но его необходимо декларировать с помощью PROPS.
Через API предоставления/внедрения любое значение может быть передано непосредственно на любой уровень подкомпонентов напрямую через согласованный ключ.
// index.vue
import {ref, provide} from 'vue';
import dayjs from "dayjs";
export default {
setup() {
const date = ref(dayjs().toDate());
const setDate = (value) => {
date.value = dayjs(value).toDate();
};
const displayMode = ref("date");
const setDisplayMode = (mode) => {
displayMode.value = mode;
};
provide("displayMode", [displayMode, setDisplayMode]);
provide("date", [date, setDate]);
return {}
}
}
Подкомпоненты могут напрямую получать значения и сеттер-функции, объявленные в индексе, через инъекционный API.
Компонент таблицы календаря
Давайте сначала напишем календарь
// DatePanel.vue
import { computed, h, inject } from "vue";
import dayjs from "dayjs";
export default {
setup() {
const [date, setDate] = inject("date");
const dateList = computed(() => getDateList(date.value));
return {
dateList,
weeks: ["日", "一", "二", "三", "四", "五", "六"],
};
},
}
// 生成日历表
function getDateList(date) {
// 本月第一天
const day0 = dayjs(date).startOf("month");
// 本月第一个星期的星期日
const firstDay = day0.subtract(day0.get("day"), "day");
const rows = 6; //Math.ceil((day0.get("day") + day0.daysInMonth()) / 7);
return Array(rows * 7)
.fill(0)
.map((n, i) => firstDay.add(i, "day"));
}
Календарная таблица win10 начинается с понедельника, который является 1-м номером недели в js.Для удобства расчета мы начинаем с воскресенья, который является 0-м номером недели в js.
Для расчета календарной таблицы нам нужно только вычислить дату воскресенья первой недели этого месяца, а затем начать с этого дня и последовательно увеличивать, чтобы получить всю календарную таблицу.
В целях обеспечения высокой стабильности календаря он фиксируется на 6 недель, что составляет 42 дня.
Части шаблона и css не учитываются. Заинтересованы в посещении напрямуюисходный код
Маска прожектора
С базовой раскладкой вы можете начать писать маску.
Давайте попробуем функциональный компонент для этого компонента. Предполагается, что функциональные компоненты используются непосредственно в качестве установочной части компонента. Не ошибитесь, если не читали исходный код.
Функция может напрямую возвращать jsx.Не знаю, есть ли проблема с парсингом jsx на данном этапе.Неродные атрибуты нельзя передать через jsx, а можно получить только единообразно через атрибут attrs.
К счастью, шаблонная часть этого компонента очень проста, а динамическая часть — это только стиль.
Согласно вышеприведенному анализу, прожектор на самом деле является маской для рытья дыр, и этот эффект может быть достигнут с помощью радиальных градиентов.
radial-gradient(transparent, rgba(0, 0, 0, 1) 60px, #000)
Чтобы заставить его анимироваться, нам нужно получить координаты мыши на элементе в реальном времени.
Такая функция у официалов очень даже годнаяпример, вы можете использовать его, если измените его.
Мы также абстрагируем эту функцию в хук.
// src/hooks/useMousePosition.js
import { onMounted, onUnmounted, toRefs, reactive } from "vue";
// 传入一个dom引用,鼠标移入该元素时,派发鼠标在该元素上的位置
export default function (elRef) {
const state = reactive({
x: 0,
y: 0,
width: 0,
height: 0,
enter: false,
});
let rect = {
top: 0,
left: 0,
width: 0,
height: 0,
};
function onEnter() {
if (!elRef || !elRef.value) return;
state.enter = true;
rect = elRef.value.getBoundingClientRect();
state.height = rect.height;
state.width = rect.width;
}
function onMove(e) {
const { clientX, clientY } = e;
state.x = clientX - rect.left;
state.y = clientY - rect.top;
}
function onLeave() {
state.enter = false;
}
onMounted(() => {
if (!elRef || !elRef.value) return;
elRef.value.addEventListener("mouseenter", onEnter);
elRef.value.addEventListener("mousemove", onMove);
elRef.value.addEventListener("mouseleave", onLeave);
});
onUnmounted(() => {
if (!elRef || !elRef.value) return;
elRef.value.removeEventListener("mousemove", onMove);
elRef.value.removeEventListener("mouseenter", onEnter);
elRef.value.removeEventListener("mouseleave", onLeave);
});
return {
...toRefs(state),
};
}
Измените index.vue.
import useMousePosition from "@/hooks/useMousePosition";
export default {
setup(){
const el = ref(null);
const position = useMousePosition(el);
return {
el
}
}
}
Раздел шаблона
<div class="calendar" ref="el">
<!--other-->
<Mask :position="position"/>
</div>
Обратите внимание, что ссылка на ссылку в шаблоне не может быть добавлена
:, Очень легко подумать, что шаблону нужно привязать ссылку el в настройках к элементу, но на самом деле нужно только привязать строку el к ref элемента.✅❌При информации о размере координаты, предоставленной родительской компонентом, компонент маски может быть анимирован.
Так как Маска будет перемещаться в родительском компоненте, когда она перемещается к краю, она может быть открыта, поэтому ее можно увеличить до размера родительского компонента в 2 раза.
В то же время, чтобы его центр перекрывал мышь, он должен смещаться на 1/2 своего размера влево вверх.// Mask.js import { computed, h } from "vue"; // 使用jsx必须引入h export default function Mask(props) { const position = props.position; const style = computed(() => { const size = Math.max(position.width, position.height) * 2; const isEnter = position.enter; return { transform: `translate(${position.x - size / 2}px, ${ position.y - size / 2 }px)`, backgroundImage: isEnter ? `radial-gradient(transparent, rgba(0, 0, 0, 1) 60px, #000)` : "", backgroundColor: isEnter ? "" : "#000", width: size ? size + "px" : "100%", height: size ? size + "px" : "100%", }; }); return <div class="mask" style={style.value} />; }ps Отзывчивое значение, проанализированное шаблоном, автоматически передаст свое значение дочернему компоненту, поэтому значение, полученное с помощью реквизита, не нужно добавлять с .value.
Компоненты рабочей области
Теперь есть базовые эффекты, а также классные эффекты при перемещении мыши. Приступим к его совершенствованию.
Сдвиньте переднюю часть к компоненту CalendarHead.
Здесь есть две основные операции: одна — переключение вида, а другая — корректировка даты вверх и вниз.
index открыл чтение и запись этих двух значений в дочерние компоненты через Provide.
Чтобы переключить представления, просто проверьте текущий вид и продолжайте работу.
Чтобы настроить дату, также необходимо проверить вид.
Когда это таблица календаря, диапазон регулировки составляет плюс-минус один месяц;
Когда текущая таблица представляет собой месячную таблицу, диапазон корректировки составляет плюс-минус один год;
Когда текущий год является таблицей, диапазон регулировки составляет плюс-минус шестнадцать лет;import {inject, ref, computed} from "vue"; import dayjs from 'dayjs' export default { setup() { const [displayMode, setDisplayMode] = inject("displayMode", [ref("date"), (v) => v]); const [date, setDate] = inject("date", [ref(new Date()), (v) => v]); const setPanelMode = () => { let mode = "date"; if (displayMode.value === "date") { mode = "month"; } if (displayMode.value === "month") { mode = "year"; } if (displayMode.value === "year") { mode = "year"; } setDisplayMode(mode); }; const dateString = computed(() => (displayMode.value === "date" ? fmtDate : fmtYear)(date.value)); const handleDate = (isAdd) => () => { const setMap = { date: { value: 1, unit: "month", }, month: { value: 1, unit: "year", }, year: { value: 16, unit: "year", }, }; const setter = setMap[displayMode.value]; const value = isAdd ? dayjs(date.value).add(setter.value, setter.unit) : dayjs(date.value).subtract(setter.value, setter.unit); setDate(value.toDate()); }; const upward = handleDate(false); const downward = handleDate(true); return { upward, downward, setPanelMode, dateString, } } }компонент месяц/год
Теперь операция может переключать месяцы в таблице календаря, а затем добавлять таблицу месяцев/годов.
Сначала измените index.vue, отреагируйте на изменение режима отображения и подготовьтесь к анимации.
Здесь мы используем watch из API композиции, чтобы активно отслеживать изменения реактивных значений. При изменении режима отображения поменяйте местами имя компонента и имя перехода.Есть два вида анимаций, отступ при переходе на верхний уровень и расширение при переходе на нижний уровень, которые можно получить, наблюдая за старым и новым режимами просмотра.
// index.vue export default { setup() { // other code const transitionName = ref("out"); const componentName = ref("date-panel"); const levels = ["date", "month", "year"]; watch(displayMode, (now, old) => { const nowLevel = levels.indexOf(now); const oldLevel = levels.indexOf(old); transitionName.value = nowLevel < oldLevel ? "out" : "in"; // componentName必须在transitionName设置之后或同时设置才不会使transitionName滞后 componentName.value = { date: "date-panel", month: "month-panel", year: "year-panel", }[displayMode.value]; }); return { componentName, transitionName, } } }Раздел шаблона
<div class="calendar" ref="el"> <CalendarHead/> <div class="cell-wrap"> <transition :name="transitionName"> <component :is="componentName"/> </transition> </div> <Mask :position="position"/> </div>Вернемся к теме.
На самом деле, таблица месяцев и таблица годов также имеют операцию.
Щелкните таблицу месяцев, переключите дату на выбранный год и месяц и переключите представление на таблицу календаря.
Щелкните таблицу года, переключите дату на выбранный год и переключите представление на таблицу месяца.
Это легко сделать с помощью displayMode и даты, делегированных index.vue.// MonthPanel import { computed, inject, ref } from "vue"; import dayjs from "dayjs"; export default { setup() { const [date, setDate] = inject("date", [ref(new Date()), (v) => v]); const [displayMode, setDisplayMode] = inject("displayMode", [ef("date"),(v) => v]); const monthList = computed(() => { const month0 = dayjs(date.value).month(0); return Array(16) .fill(0) .map((n, i) => month0.add(i, "month")); }); const getClass = (item) => { return [ "month-cell", item.month() === dayjs(date.value).month() && " current-month ", item.year() === dayjs(date.value).year() && " current-year ", ].join(" "); }; const pickMonth = (item) => { setDate(item.toDate()); setDisplayMode("date"); }; return { monthList, getClass, pickMonth, }; }, };Годовая таблица почти такая же, поэтому я не упущу ее, если вам интересно, вы можете проверить это непосредственно.Исходный код годовой таблицыа такжеЕжемесячный исходный код
Суммировать
Такая маленькая игрушка в основном использует API, обычно используемые vue3. В процессе написания тоже много сложностей, кто-то не знаком с новым API, кто-то препятствиями в знаниях, а кто-то незрелостью самого vue3. Конечно, трудности с изучением чего-то нового — это хорошо. Преодолеть это или получить что-то.
В общем, vue3 имеет большой потенциал, особенно концепция хуков имеет большой потенциал.В React уже есть библиотека, такая как swr.Возможно, что в будущем можно будет инкапсулировать некоторую утомительную и сложную логику.Преимущества,конечно, , недостатки могут заключаться в том, что разработчики менее подвержены влиянию вещей более низкого уровня.
Некоторые люди думают, что такие библиотеки, как react/vue, заставили многих новых разработчиков больше не знать, как работать с dom.В будущем эта ситуация может усугубиться.Разработчики не умеют писать подтягивающие нагрузки, умеют только npm install ... Вернемся к вью. Дизайн API-интерфейса композиции может устранить необходимость в написании строк или использовании облегченных библиотек рендеринга, таких как preact, при разработке некоторых небольших компонентов. И его можно легко портировать на другие концы.По оценкам, куча фреймворков апплетов будет снова занята обновлением (опять же, идея использования собственного апплета...).
Не говоря уже о плохом опыте.
- Дизайн ref.value немного раздражает, его легко запутать, а иногда и необходимо
.value, иногда не нужно. И нет ли лучшего способа перехватить базовое значение, кроме упаковки? Может быть, обертывание — не единственное решение, например, обертывание текущего объекта модуля или контекста напрямую? Не верьте мне (смеется)- Поддержка ts недостаточно хороша.Вначале я ходил сразу на ts.Однако не все было гладко,и было много незнакомых мест.Для того,чтобы лучше устранить проблему,я вернулся к js. Все еще недостаточно из коробки.
- Ссылка в шаблоне может быть совместима со старым использованием, и она по-прежнему является строкой, но было бы лучше, если бы она поддерживала привязку значений ссылки, иначе будет путаница.
адрес проекта