In response to this facebook post. I created a simple bus seat reservation user interface using Vue.js and SVG. I started by preparing an SVG image of the bus seat and then render the SVG elements using Javascript (VueJS). You can use a raster image (sprite) for this but I prefer to use vector graphics (SVG) because it allows me to control the stroke and background color programmatically.
The bus seat vector image looks like this
The SVG code for the bus seat is shown below
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<title>Bus Seat</title>
<path class="cls-ra" d="M36,17.3H80.4a8.88,8.88,0,0,1,6.72-7.25A5.77,5.77,0,0,0,81.57,6H36a5.72,5.72,0,0,0-5.76,5.66A5.71,5.71,0,0,0,36,17.3Z"/>
<path class="cls-ra" d="M80.29,82.79H36A5.66,5.66,0,1,0,36,94.1H81.47a6.13,6.13,0,0,0,5.44-3.41A8.77,8.77,0,0,1,80.29,82.79Z"/>
<path class="cls-ra" d="M80.08,79.7V20.5H35.92A8.85,8.85,0,0,1,27.17,13h-18a4,4,0,0,0-4.06,4V82.79a4,4,0,0,0,4.06,3.95H27.28a8.65,8.65,0,0,1,8.75-7Z"/>
<path class="cls-ra" d="M89.15,12.93a5.71,5.71,0,0,0-5.76,5.65V82.15a5.76,5.76,0,0,0,11.52,0V18.58A5.71,5.71,0,0,0,89.15,12.93Z"/>
<path class="cls-ra" d="M90.21,9.94a8.93,8.93,0,0,0-8.74-7H36a8.94,8.94,0,0,0-8.75,6.93H9.15A7.22,7.22,0,0,0,2,17V82.79a7.06,7.06,0,0,0,7.15,7h18a8.85,8.85,0,0,0,8.75,7.26H81.47A8.91,8.91,0,0,0,90,90.9a8.81,8.81,0,0,0,8-8.75V18.58A8.84,8.84,0,0,0,90.21,9.94ZM36,6H81.57a5.77,5.77,0,0,1,5.55,4.06A8.88,8.88,0,0,0,80.4,17.3H36a5.71,5.71,0,0,1-5.76-5.65A5.72,5.72,0,0,1,36,6ZM27.28,86.74H9.15a4,4,0,0,1-4.06-3.95V17a4,4,0,0,1,4.06-4h18a8.85,8.85,0,0,0,8.75,7.47H80.08V79.7H36A8.65,8.65,0,0,0,27.28,86.74ZM81.47,94.1H36a5.66,5.66,0,1,1,0-11.31H80.29a8.77,8.77,0,0,0,6.62,7.9A6.13,6.13,0,0,1,81.47,94.1ZM94.91,82.15a5.76,5.76,0,0,1-11.52,0V18.58a5.76,5.76,0,0,1,11.52,0Z"/>
</svg>
Interacting with SVG elements using Vue.js
Let us analyze the index.html for better understand how it works
34<body>
35<div id="root">
36 <div class="container">
37 <div class="row">
38 <div class="col-8 py-5">
39 <div>
40 <table class="mx-auto">
41 <tr v-for="idxr, r in rows">
42 <td v-for="idxc, c in cols" class="pl-2" style="width: 50px">
43 <svg @click="onSeatSelected(idxr, idxc)"
44 v-if="!isAisle(idxr, idxc)"
45 id="Layer_1" data-name="Layer 1"
46 xmlns="http://www.w3.org/2000/svg"
47 viewBox="0 0 100 100">
48 <path :class="classifier(idxr, idxc)" d="M36,17.3H80.4a8.88,8.88,0,0,1,6.72-7.25A5.77,5.77,0,0,0,81.57,6H36a5.72,5.72,0,0,0-5.76,5.66A5.71,5.71,0,0,0,36,17.3Z"/>
49 <path :class="classifier(idxr, idxc)" d="M80.29,82.79H36A5.66,5.66,0,1,0,36,94.1H81.47a6.13,6.13,0,0,0,5.44-3.41A8.77,8.77,0,0,1,80.29,82.79Z"/>
50 <path :class="classifier(idxr, idxc)" d="M80.08,79.7V20.5H35.92A8.85,8.85,0,0,1,27.17,13h-18a4,4,0,0,0-4.06,4V82.79a4,4,0,0,0,4.06,3.95H27.28a8.65,8.65,0,0,1,8.75-7Z"/>
51 <path :class="classifier(idxr, idxc)" d="M89.15,12.93a5.71,5.71,0,0,0-5.76,5.65V82.15a5.76,5.76,0,0,0,11.52,0V18.58A5.71,5.71,0,0,0,89.15,12.93Z"/>
52 <path :class="classifier(idxr, idxc)" d="M90.21,9.94a8.93,8.93,0,0,0-8.74-7H36a8.94,8.94,0,0,0-8.75,6.93H9.15A7.22,7.22,0,0,0,2,17V82.79a7.06,7.06,0,0,0,7.15,7h18a8.85,8.85,0,0,0,8.75,7.26H81.47A8.91,8.91,0,0,0,90,90.9a8.81,8.81,0,0,0,8-8.75V18.58A8.84,8.84,0,0,0,90.21,9.94ZM36,6H81.57a5.77,5.77,0,0,1,5.55,4.06A8.88,8.88,0,0,0,80.4,17.3H36a5.71,5.71,0,0,1-5.76-5.65A5.72,5.72,0,0,1,36,6ZM27.28,86.74H9.15a4,4,0,0,1-4.06-3.95V17a4,4,0,0,1,4.06-4h18a8.85,8.85,0,0,0,8.75,7.47H80.08V79.7H36A8.65,8.65,0,0,0,27.28,86.74ZM81.47,94.1H36a5.66,5.66,0,1,1,0-11.31H80.29a8.77,8.77,0,0,0,6.62,7.9A6.13,6.13,0,0,1,81.47,94.1ZM94.91,82.15a5.76,5.76,0,0,1-11.52,0V18.58a5.76,5.76,0,0,1,11.52,0Z"/>
53 </svg>
54 </td>
55 </tr>
56 </table>
57 </div>
58 </div>
59 <div class="col-4 pt-3">
60 <div class="card" v-show="selectedSeat != null" style="display: none">
61 <div class="card-header">
62 Properties
63 </div>
In line 43, The code @click=“onSeatSelected(idxr, idxc)
binds the SVG to a mouse-click event handler.
From line 48 to 52, The code :class=“classifier(idxr, idxc)"
handles the binding that controls the background and line color.
109<script type="text/javascript">
110let app = new Vue({
111 el: "#root",
112 data: {
113 errors: [],
114 o: [],
115 selectedSeat: null,
116 rows: 5,
117 cols: 12,
118 seats: [],
119 },
120 computed: {
121 },
122 methods:
123 {
124 getSeat(r, c) {
125 for(let i = 0; i < this.seats.length; ++i) {
126 if(this.seats[i].position.r == r && this.seats[i].position.c == c) {
127 return this.seats[i];
128 }
129 }
130 return null;
131 },
132 generateSeats() {
133 for(let y = 1; y <= this.rows; ++y){
134 for(let x = 1; x <= this.cols; ++x) {
135 if(!this.isAisle(y, x)) {
136 this.seats.push({
137 position: {r: y, c: x},
138 status: "RA",
139 });
140 }
141 }
142 }
143 },
144 classifier(r, c){
145 let seat = this.getSeat(r,c);
146 if(seat != null){
147 if(this.selectedSeat != seat) {
148 switch(seat.status) {
149 case 'RA':
150 return 'cls-ra';
151 case 'RB':
152 return 'cls-rb';
153 case 'FA':
154 return 'cls-fa';
155 case 'FB':
156 return 'cls-fb';
157 case 'MA':
158 return 'cls-ma';
159 case 'MB':
160 return 'cls-mb';
161 default:
162 return 'cls-ra';
163 }
164 } else {
165 return 'cls-selected';
166 }
167 }
168 },
169 isAisle(r, c) {
170 if(r == 3){
171 if(c >= 1 && c <= 11) {
172 return true;
173 }
174 }
175 return false;
176 },
177 onSeatSelected(r,c) {
178 if(this.selectedSeat == this.getSeat(r, c)) {
179 this.selectedSeat = null;
180 } else {
181 this.selectedSeat = this.getSeat(r, c);
182 }
183 },
184 seatStatus(status){
185 if(this.selectedSeat != null) {
186 if(this.selectedSeat.status == status)
187 return 'active';
188 }
189 return '';
190 },
191 changeSeatStatus(status) {
192 if(this.selectedSeat != null) {
193 for(let i = 0; i < this.seats.length; ++i) {
194 if(this.seats[i].position.r == this.selectedSeat.position.r && this.seats[i].position.c == this.selectedSeat.position.c) {
195 this.seats[i].status = status;
196 this.selectedSeat = null;
197 break;
198 }
199 }
200 }
201 },
202 },
203 beforeMount() {
204 },
205 mounted() {
206 this.generateSeats();
207 }
208});
209</script>
The classifier method implementation from line 144 to 168 handles the class setting of the seat image.
Demo
https://johnpili.com/demo/bus-seat-reservation-ui-concept-vuejs/Video
Source Code
Get the source code here
https://github.com/johnpili/bus-seat-reservation-ui-concept-vuejs
Conclusion
Using Javascript framework like Vue.js makes it easy for developers to create an interactive HTML user interfaces. The next thing to do now is to integrate this UI to PHP, Java, Go, or Python to save the information to the database.