Skip to content

Commit 909390b

Browse files
committed
切割线面板 UI
1 parent add572e commit 909390b

File tree

2 files changed

+221
-21
lines changed

2 files changed

+221
-21
lines changed

src/editor/inputs/subblocks/derivatePane.vue

Lines changed: 212 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,52 +2,130 @@
22
<div class="derivate-pane">
33
<s-tab mode="fixed" v-model.lazy="tab">
44
<s-tab-item value="derivate">
5-
<div slot="text">切线设置</div>
5+
<div slot="text" class="tab-text">
6+
切线设置
7+
<Transition name="badge">
8+
<s-badge class="tab-badge" v-if="derivateEnabled"></s-badge>
9+
</Transition>
10+
</div>
611
</s-tab-item>
712
<s-tab-item value="secant">
8-
<div slot="text">割线设置</div>
13+
<div slot="text" class="tab-text">
14+
割线设置
15+
<Transition name="badge">
16+
<s-badge class="tab-badge" v-if="secantEnabled"></s-badge>
17+
</Transition>
18+
</div>
919
</s-tab-item>
1020
</s-tab>
1121

1222
<!-- derivate -->
13-
<div class="content" v-if="tab === 'derivate'">
14-
<div class="field">
23+
<div
24+
class="content"
25+
v-if="tab === 'derivate'"
26+
:class="{ enabled: derivateEnabled }"
27+
>
28+
<div class="field no-opacity">
1529
<div class="label">启用切线显示</div>
1630
<s-switch type="checkbox" v-model.lazy="derivateEnabled"></s-switch>
1731
</div>
1832
<div class="field main-fn">
19-
<span class="label styled">y'=</span>
33+
<span class="label wide styled">y'=</span>
2034
<FunctionField class="styled fn" label="f'(x)" v-model="derivateFn" />
2135
</div>
2236
<div class="field">
2337
<div class="label">切点位置</div>
24-
<s-segmented-button value="b">
25-
<s-segmented-button-item value="a"> 指定切点 </s-segmented-button-item>
26-
<s-segmented-button-item value="b"> 跟随鼠标 </s-segmented-button-item>
38+
<s-segmented-button v-model.lazy="derivFollowMouseStr">
39+
<s-segmented-button-item value="followMouse">
40+
跟随鼠标
41+
</s-segmented-button-item>
42+
<s-segmented-button-item value="manual">
43+
指定切点
44+
</s-segmented-button-item>
2745
</s-segmented-button>
2846
</div>
29-
<div class="field">
30-
<div class="label">切点横坐标</div>
31-
<s-text-field type="number" label="x" class="styled"></s-text-field>
32-
</div>
47+
<s-fold :folded="derivFollowMouse" class="derivate-x-coord-fold">
48+
<div class="field x-coord-field">
49+
<div class="label">切点横坐标</div>
50+
<s-text-field
51+
class="x-coord styled"
52+
type="number"
53+
label="x"
54+
v-model="derivateX"
55+
></s-text-field>
56+
</div>
57+
</s-fold>
3358
</div>
3459

3560
<!-- secant -->
36-
<div class="content" v-else>
37-
<div class="field">
61+
<div class="content" v-else :class="{ enabled: secantEnabled }">
62+
<div class="field no-opacity">
3863
<div class="label">启用割线显示</div>
3964
<s-switch type="checkbox" v-model.lazy="secantEnabled"></s-switch>
4065
</div>
41-
66+
<div class="secants">
67+
<AnimatedList>
68+
<AnimatedListItem v-for="(item, index) in secantArr" :key="item.key">
69+
<div class="secant-item">
70+
<s-checkbox
71+
type="checkbox"
72+
v-model="item.followMouse"
73+
class="secant-checkbox"
74+
>
75+
跟随鼠标
76+
</s-checkbox>
77+
<s-text-field
78+
class="sec-coord styled-inner"
79+
type="number"
80+
label="x₁"
81+
v-model="item.x1"
82+
></s-text-field>
83+
<Transition name="field-squeeze">
84+
<s-text-field
85+
v-if="!item.followMouse"
86+
class="sec-coord styled-inner"
87+
type="number"
88+
label="x₂"
89+
v-model="item.x2"
90+
></s-text-field>
91+
</Transition>
92+
<s-icon-button
93+
@click="secantArr.splice(index, 1)"
94+
class="secant-delete"
95+
>
96+
<SIconDelete />
97+
</s-icon-button>
98+
</div>
99+
</AnimatedListItem>
100+
</AnimatedList>
101+
</div>
102+
<s-button
103+
class="add-secant"
104+
type="text"
105+
@click="
106+
secantArr.push({
107+
key: Math.random(),
108+
followMouse: true,
109+
x1: 0,
110+
x2: '',
111+
})
112+
"
113+
>
114+
<s-icon slot="start" name="add" /> 添加
115+
</s-button>
42116
</div>
43117
</div>
44118
</template>
45119

46120
<script lang="ts" setup>
47121
import { PrivateDataTypes } from "@/types/data";
48-
import { ref, toRef } from "vue";
122+
import { computed, reactive, ref, toRef } from "vue";
49123
import FunctionField from "./function.vue";
50124
125+
import AnimatedList from "@/ui/animated/animatedList.vue";
126+
import AnimatedListItem from "@/ui/animated/animatedListItem.vue";
127+
import SIconDelete from "@/ui/icons/delete.vue";
128+
51129
const props = defineProps<{
52130
self: PrivateDataTypes.Linear;
53131
}>();
@@ -57,28 +135,143 @@ const self = toRef(props, "self");
57135
58136
const derivateEnabled = ref(false);
59137
const derivateFn = ref("");
138+
const derivFollowMouseStr = ref("followMouse");
139+
const derivFollowMouse = computed(
140+
() => derivFollowMouseStr.value === "followMouse"
141+
);
142+
const derivateX = ref(0);
143+
60144
const secantEnabled = ref(false);
145+
const secantArr = reactive<
146+
{
147+
key: number;
148+
followMouse: boolean;
149+
x1: number | "";
150+
x2: number | "";
151+
}[]
152+
>([]);
61153
</script>
62154

63155
<style lang="scss">
64156
.derivate-pane {
65157
width: 25em;
158+
s-tab {
159+
background-color: var(--s-color-surface-container-low);
160+
}
161+
.tab-text {
162+
display: flex;
163+
align-items: center;
164+
}
165+
.tab-badge {
166+
background-color: var(--s-color-success);
167+
color: var(--s-color-on-success);
168+
margin-left: 0.8em;
169+
}
170+
.badge {
171+
&-enter-active,
172+
&-leave-active {
173+
transition:
174+
opacity 0.1s,
175+
margin-left 0.5s cubic-bezier(0, 1, 0, 1);
176+
}
177+
&-leave-to,
178+
&-enter-from {
179+
opacity: 0;
180+
margin-left: -8px;
181+
}
182+
}
66183
.content {
67-
padding: 15px;
184+
padding: 20px;
68185
display: flex;
69186
flex-direction: column;
70-
gap: 0.5em;
187+
gap: 0.8em;
188+
&.enabled {
189+
.field,
190+
.secants,
191+
.add-secant {
192+
opacity: 1;
193+
}
194+
}
195+
.field,
196+
.secants,
197+
.add-secant {
198+
opacity: 0.5;
199+
transition: opacity 0.2s;
200+
}
71201
}
72202
.field {
73203
display: flex;
74204
justify-content: space-between;
75205
align-items: center;
76206
}
207+
.no-opacity {
208+
opacity: 1 !important;
209+
}
77210
.field.main-fn {
78211
font-size: 1.5em;
79-
.label {
212+
.label.wide {
80213
width: 2.2em;
81214
}
82215
}
216+
.x-coord.styled {
217+
font-size: 20px;
218+
max-width: 10em;
219+
}
220+
.derivate-x-coord-fold {
221+
margin-top: -0.8em;
222+
.x-coord-field {
223+
margin-top: 0.8em;
224+
}
225+
}
226+
.secants {
227+
display: flex;
228+
flex-direction: column;
229+
margin: 0 -0.5em 0 -1em;
230+
}
231+
.secant-item {
232+
display: flex;
233+
align-items: center;
234+
margin: 0.3em 0;
235+
padding-left: 1em;
236+
gap: 0.5em;
237+
}
238+
.sec-coord {
239+
width: 0;
240+
flex-grow: 1;
241+
font-size: 20px !important;
242+
}
243+
.secant-delete {
244+
flex-shrink: 0;
245+
margin-left: -0.2em;
246+
}
247+
.secant-checkbox {
248+
margin-right: 0.3em;
249+
}
250+
.field-squeeze {
251+
&-enter-active,
252+
&-leave-active {
253+
transition:
254+
flex-grow 0.3s cubic-bezier(0.075, 0.82, 0.165, 1),
255+
width 0.3s cubic-bezier(0.075, 0.82, 0.165, 1),
256+
margin-left 0.35s 0.05s;
257+
min-width: 0;
258+
overflow-x: hidden;
259+
}
260+
&-enter-active {
261+
transition:
262+
flex-grow 0.3s var(--s-motion-easing-emphasized),
263+
width 0.3s var(--s-motion-easing-emphasized),
264+
margin-left 0.05s;
265+
}
266+
&-enter-from,
267+
&-leave-to {
268+
flex-grow: 0 !important;
269+
width: 0 !important;
270+
margin-left: -0.5em;
271+
}
272+
}
273+
.add-secant {
274+
margin-top: -0.3em;
275+
}
83276
}
84277
</style>

src/ui/animated/animatedListItem.vue

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
<template>
2-
<div class="animatedListItem" :class="$props.class">
3-
<div class="animatedListItemInner">
2+
<div class="animatedListItem" :class="props.class">
3+
<div class="animatedListItemInner" :class="props.classInner">
44
<slot></slot>
55
</div>
66
</div>
77
</template>
88

9+
<script lang="ts" setup>
10+
const props = defineProps<{
11+
class?: string;
12+
classInner?: string;
13+
}>();
14+
</script>
15+
916
<style>
1017
.animatedListItem {
1118
display: grid;

0 commit comments

Comments
 (0)