mirror of
https://github.com/Snigdha-OS/Snigdha-OS.github.io.git
synced 2025-09-07 05:05:18 +02:00
🎨 style(new!): add new ui vue.js
Signed-off-by: Abhiraj Roy <157954129+iconized@users.noreply.github.com>
This commit is contained in:
99
components/AppFooter.vue
Normal file
99
components/AppFooter.vue
Normal file
@@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<footer class='flex md:justify-between border-top text-menu-text font-fira_retina'>
|
||||
|
||||
<!-- social icons -->
|
||||
<div class="w-full flex justify-between md:justify-start">
|
||||
<span id="social-title" class="h-full flex justify-center items-center border-right px-5">
|
||||
find me in:
|
||||
</span>
|
||||
<div id="social-icons" class="flex">
|
||||
<NuxtLink :to="social.twitter.url + social.twitter.user" target="_blank" class="flex justify-center items-center">
|
||||
<img src="/icons/social/twitter.svg"/>
|
||||
</NuxtLink>
|
||||
<NuxtLink :to="social.facebook.url + social.facebook.user" target="_blank" class="flex justify-center items-center">
|
||||
<img src="/icons/social/facebook.svg"/>
|
||||
</NuxtLink>
|
||||
<NuxtLink :to="social.github.url + social.github.user" target="_blank" class="flex md:hidden justify-center items-center">
|
||||
<img src="/icons/social/github.svg"/>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- github user -->
|
||||
<NuxtLink :to="social.github.url + social.github.user" target="_blank" class="hidden md:flex items-center px-5 border-left">
|
||||
@{{ social.github.user }}
|
||||
<img src="/icons/social/github.svg"/>
|
||||
</NuxtLink>
|
||||
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
|
||||
footer {
|
||||
height: 40px;
|
||||
min-height: 40px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
footer a:hover {
|
||||
background-color: #1e2d3d74;
|
||||
}
|
||||
|
||||
#social-icons > a {
|
||||
border-right: 1px solid #1E2D3D;
|
||||
height: 100%;
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
#social-icons > a > img {
|
||||
width: 1.25rem; /* 20px */
|
||||
height: 1.25rem; /* 20px */
|
||||
margin: auto;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
footer > a > img {
|
||||
width: 1.25rem; /* 20px */
|
||||
height: 1.25rem; /* 20px */
|
||||
margin-left: 0.5rem; /* 8px */
|
||||
}
|
||||
|
||||
#social-icons > a:hover img {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
|
||||
#social-title {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
#social-icons > a {
|
||||
border-right: none;
|
||||
border-left: 1px solid #1E2D3D;
|
||||
}
|
||||
|
||||
#social-icons > a > img {
|
||||
width: 1.5rem; /* 20px */
|
||||
height: 1.5rem; /* 20px */
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AppFooter',
|
||||
data() {
|
||||
return {
|
||||
route: this.$route.path,
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
return {
|
||||
social: useRuntimeConfig().dev.contacts.social
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
101
components/AppHeader.vue
Normal file
101
components/AppHeader.vue
Normal file
@@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<header id="navbar" class="w-full hidden lg:flex flex-col">
|
||||
<nav class="w-full flex justify-between border-bot">
|
||||
<github-corner url="https://github.com/alexdeploy/developer-portfolio-v2" />
|
||||
<div class="flex">
|
||||
<NuxtLink id="nav-logo" to="/">
|
||||
{{ config.dev.logo_name }}
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink id="nav-link" to="/" :class="{ active: isActive('/') }">
|
||||
_hello
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink id="nav-link" to="/about-me" :class="{ active: isActive('/about-me') }">
|
||||
_about-me
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink id="nav-link" to="/projects" :class="{ active: isActive('/projects') }">
|
||||
_projects
|
||||
</NuxtLink>
|
||||
</div>
|
||||
|
||||
<NuxtLink id="nav-link-contact" to="/contact-me" :class="{ active: isActive('/contact-me')}">
|
||||
_contact-me
|
||||
</NuxtLink>
|
||||
|
||||
</nav>
|
||||
|
||||
</header>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GithubCorner from './GithubCorner.vue';
|
||||
export default {
|
||||
name: 'AppHeader',
|
||||
components: {
|
||||
GithubCorner
|
||||
},
|
||||
computed: {
|
||||
// Set active class to current page link
|
||||
isActive() {
|
||||
return route => this.$route.path === route;
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
const config = useRuntimeConfig()
|
||||
|
||||
return {
|
||||
config
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
#nav-link {
|
||||
border-right: 1px solid #1E2D3D;
|
||||
@apply text-menu-text font-fira_retina px-6 h-full flex items-center;
|
||||
}
|
||||
|
||||
#nav-link-contact {
|
||||
border-left: 1px solid #1E2D3D;
|
||||
@apply text-menu-text font-fira_retina px-6 h-full flex items-center;
|
||||
}
|
||||
|
||||
#nav-link:hover, #nav-link-contact:hover {
|
||||
background-color: #1e2d3d74;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#nav-logo {
|
||||
border-right: 1px solid #1E2D3D;
|
||||
@apply text-menu-text font-fira_retina px-6 h-full flex items-center;
|
||||
}
|
||||
|
||||
#nav-logo:hover {
|
||||
background-color: #1e2d3d74;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#nav-link.router-link-active, #nav-link-contact.router-link-active {
|
||||
border-bottom: 2px solid #FEA55F;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#nav-logo.router-link-active {
|
||||
border-right: 1px solid #1E2D3D;
|
||||
border-bottom: none;
|
||||
@apply text-menu-text;
|
||||
}
|
||||
|
||||
#navbar > nav {
|
||||
height: 45px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
72
components/CommentedText.vue
Normal file
72
components/CommentedText.vue
Normal file
@@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<div class="code-container flex font-fira_retina text-menu-text">
|
||||
<div class="line-numbers lg:flex flex-col w-32 hidden">
|
||||
|
||||
<!-- line numbers and asteriscs -->
|
||||
<div v-for="n in lines" class="grid grid-cols-2 justify-end" :key="n">
|
||||
<span class="col-span-1 mr-3">{{ n }}</span>
|
||||
<div v-if="n == 1" class="col-span-1 flex justify-center">/**</div>
|
||||
<div class="col-span-1 flex justify-center" v-if="n > 1 && n < lines">*</div>
|
||||
<div class="col-span-1 flex justify-center pl-2" v-if="n == lines">*/</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- text -->
|
||||
<div class="text-container">
|
||||
<p v-html="text"></p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
props: {
|
||||
text: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
lines: 0
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.updateLines();
|
||||
window.addEventListener("resize", this.updateLines);
|
||||
window.addEventListener("click", this.updateLines);
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener("resize", this.updateLines);
|
||||
window.removeEventListener("click", this.updateLines);
|
||||
},
|
||||
methods: {
|
||||
updateLines() {
|
||||
const textContainer = this.$el.querySelector(".text-container");
|
||||
const style = window.getComputedStyle(textContainer);
|
||||
const fontSize = parseInt(style.fontSize);
|
||||
const lineHeight = parseInt(style.lineHeight);
|
||||
const maxHeight = textContainer.offsetHeight;
|
||||
this.lines = Math.ceil(maxHeight / lineHeight) + 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.code-container {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.line-numbers {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.text-container {
|
||||
width: 100%;
|
||||
padding-left: 10px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
</style>
|
128
components/ContactForm.vue
Normal file
128
components/ContactForm.vue
Normal file
@@ -0,0 +1,128 @@
|
||||
<template>
|
||||
<form id="contact-form" class="text-sm">
|
||||
<div class="flex flex-col">
|
||||
<label for="name" class="mb-3">_name:</label>
|
||||
<input type="text" id="name-input" name="name" :placeholder="name" class="p-2 mb-5 placeholder-slate-600" required>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<label for="email" class="mb-3">_email:</label>
|
||||
<input type="email" id="email-input" name="email" :placeholder="email" class="p-2 mb-5 placeholder-slate-600" required>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<label for="message" class="mb-3">_message:</label>
|
||||
<textarea id="message-input" name="message" :placeholder="message" class="placeholder-slate-600" required></textarea>
|
||||
</div>
|
||||
<button id="submit-button" type="submit" class="py-2 px-4">submit-message</button>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
|
||||
export default {
|
||||
name: 'ContactForm',
|
||||
props: {
|
||||
name: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
email: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
message: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
document.getElementById("contact-form").addEventListener("submit", function(event) {
|
||||
event.preventDefault();
|
||||
const name = document.querySelector('input[name="name"]').value;
|
||||
const email = document.querySelector('input[name="email"]').value;
|
||||
const message = document.querySelector('textarea[name="message"]').value;
|
||||
|
||||
// Here the code to send the email
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
form {
|
||||
@apply font-fira_retina text-menu-text
|
||||
}
|
||||
input {
|
||||
background-color: #011221;
|
||||
border: 2px solid #1E2D3D;
|
||||
border-radius: 7px;
|
||||
|
||||
}
|
||||
/* Change Autocomplete styles in Chrome*/
|
||||
input:-webkit-autofill,
|
||||
input:-webkit-autofill:hover,
|
||||
input:-webkit-autofill:focus,
|
||||
textarea:-webkit-autofill,
|
||||
textarea:-webkit-autofill:hover,
|
||||
textarea:-webkit-autofill:focus,
|
||||
select:-webkit-autofill,
|
||||
select:-webkit-autofill:hover,
|
||||
select:-webkit-autofill:focus {
|
||||
-webkit-text-fill-color: rgb(190, 190, 190);
|
||||
transition: background-color 5000s ease-in-out 0s;
|
||||
border: 2px solid #607b96;
|
||||
}
|
||||
|
||||
#message-input {
|
||||
background-color: #011221;
|
||||
border: 2px solid #1E2D3D;
|
||||
border-radius: 7px;
|
||||
resize: none;
|
||||
height: 150px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#submit-button {
|
||||
@apply font-fira_retina text-white text-sm;
|
||||
background-color: #1E2D3D;
|
||||
border-radius: 7px;
|
||||
margin-top: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#submit-button:hover {
|
||||
background-color: #263B50;
|
||||
}
|
||||
|
||||
input:focus, #message-input:focus {
|
||||
outline: none;
|
||||
transition: none;
|
||||
border: 2px solid #607b96;
|
||||
box-shadow: #607b9669 0px 0px 0px 2px;
|
||||
}
|
||||
|
||||
#contact-form {
|
||||
max-width: 370px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (max-width: 1920px) {
|
||||
#contact-form {
|
||||
max-width: 320px;
|
||||
max-height: 400px;
|
||||
}
|
||||
#submit-button {
|
||||
/* width: 100%; */
|
||||
font-size: 12px;
|
||||
}
|
||||
textarea {
|
||||
font-size: 13px;
|
||||
max-height: 130px !important;
|
||||
}
|
||||
input {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
</style>
|
170
components/FormContentCode.vue
Normal file
170
components/FormContentCode.vue
Normal file
@@ -0,0 +1,170 @@
|
||||
<template>
|
||||
<div class="code-container flex font-fira_retina text-menu-text">
|
||||
<div class="line-numbers lg:flex flex-col w-16 hidden">
|
||||
|
||||
<!-- line numbers and asteriscs -->
|
||||
<div v-for="n in lines" class="grid grid-cols-2 justify-end" :key="n">
|
||||
<span class="col-span-1 mr-3">{{ n }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="font-fira_retina text-white text-container">
|
||||
<p>
|
||||
<span class="tag">
|
||||
const
|
||||
</span>
|
||||
<span class="tag-name">
|
||||
button
|
||||
</span>
|
||||
=
|
||||
<span class="tag-name">
|
||||
document.querySelector
|
||||
<span class="text-menu-text">
|
||||
(
|
||||
<span class="text-codeline-link">
|
||||
'#sendBtn'
|
||||
</span>
|
||||
);
|
||||
</span>
|
||||
</span>
|
||||
|
||||
</p>
|
||||
<br>
|
||||
<p class="text-menu-text">
|
||||
<span class="tag">
|
||||
const
|
||||
</span>
|
||||
<span class="tag-name">
|
||||
message
|
||||
</span>
|
||||
= {
|
||||
<br>
|
||||
<span id="name" class="tag-name">
|
||||
name
|
||||
</span>
|
||||
:
|
||||
<span class="text-codeline-link">"</span>
|
||||
<span id="name-value" class="text-codeline-link">
|
||||
{{ name }}
|
||||
</span>
|
||||
<span class="text-codeline-link">"</span>
|
||||
, <br>
|
||||
<span id="email" class="tag-name">
|
||||
email
|
||||
</span>
|
||||
:
|
||||
<span class="text-codeline-link">"</span>
|
||||
<span id="email-value" class="text-codeline-link">
|
||||
{{ email }}
|
||||
</span>
|
||||
<span class="text-codeline-link">"</span>
|
||||
, <br>
|
||||
<span id="message" class="tag-name">
|
||||
message
|
||||
</span>
|
||||
:
|
||||
<span class="text-codeline-link">"</span>
|
||||
<span id="message-value" class="text-codeline-link">
|
||||
{{ message }}
|
||||
</span>
|
||||
<span class="text-codeline-link">"</span>
|
||||
, <br>
|
||||
date:
|
||||
<span class="text-codeline-link">
|
||||
"{{ date }}"
|
||||
</span>
|
||||
<br>
|
||||
}
|
||||
</p>
|
||||
<br>
|
||||
<p>
|
||||
<span class="tag-name">
|
||||
button.addEventListener
|
||||
|
||||
<span class="text-menu-text">
|
||||
(
|
||||
<span class="text-codeline-link">
|
||||
'click'
|
||||
</span>
|
||||
), ()
|
||||
<span class="tag">
|
||||
=>
|
||||
</span>
|
||||
{
|
||||
<br>
|
||||
</span>
|
||||
form.send
|
||||
<span class="text-menu-text">(</span>
|
||||
message
|
||||
<span class="text-menu-text">); <br> })</span>
|
||||
</span>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data(){
|
||||
return {
|
||||
date: new Date().toDateString(),
|
||||
lines: 0
|
||||
}
|
||||
},
|
||||
props: {
|
||||
name: String,
|
||||
email: String,
|
||||
message: String,
|
||||
},
|
||||
mounted() {
|
||||
this.updateLines();
|
||||
window.addEventListener("resize", this.updateLines);
|
||||
window.addEventListener("input", this.updateLines);
|
||||
window.addEventListener("click", this.updateLines);
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener("resize", this.updateLines);
|
||||
window.removeEventListener("click", this.updateLines);
|
||||
window.addEventListener("input", this.updateLines);
|
||||
},
|
||||
methods: {
|
||||
updateLines() {
|
||||
const textContainer = this.$el.querySelector(".text-container");
|
||||
const style = window.getComputedStyle(textContainer);
|
||||
const fontSize = parseInt(style.fontSize);
|
||||
const lineHeight = parseInt(style.lineHeight);
|
||||
const maxHeight = textContainer.offsetHeight;
|
||||
this.lines = Math.ceil(maxHeight / lineHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.tag {
|
||||
color: #C98BDF;
|
||||
}
|
||||
|
||||
.tag-name{
|
||||
color: #5565E8;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
color: #F8F8F8;
|
||||
}
|
||||
|
||||
.code-container {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.line-numbers {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.text-container {
|
||||
width: 100%;
|
||||
padding-left: 0px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
</style>
|
169
components/GistSnippet.vue
Normal file
169
components/GistSnippet.vue
Normal file
@@ -0,0 +1,169 @@
|
||||
<template>
|
||||
<div class="gist mb-5" v-if="dataFetched">
|
||||
|
||||
<!-- head info -->
|
||||
<div class="flex justify-between my-2">
|
||||
|
||||
<div class="flex">
|
||||
<!-- avatar -->
|
||||
<img :src="gist.owner.avatar_url" alt="" class="w-8 h-8 rounded-full mr-2">
|
||||
|
||||
<!-- username & gist date info -->
|
||||
<div class="flex flex-col">
|
||||
<a id="username" :href="'https://github.com/' + gist.owner.login" target="_blank" class="font-fira_bold text-purple-text text-xs pb-1 hover:cursor-pointer">
|
||||
@{{ gist.owner.login }}
|
||||
</a>
|
||||
<p class="font-fira_retina text-xs text-menu-text">Created {{ monthsAgo }} months ago</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- details and stars -->
|
||||
<div class="flex text-menu-text font-fira_retina text-xs justify-self-end lg:mx-2">
|
||||
<div class="flex lg:mx-2 hover:cursor-pointer hover:text-white">
|
||||
<img src="/icons/gist/comments.svg" alt="" class="w-4 h-4 mr-2">
|
||||
<span @click="showComment(gist.id)">details</span>
|
||||
</div>
|
||||
<div class="hidden lg:flex hover:cursor-pointer hover:text-white">
|
||||
<img src="/icons/gist/star.svg" alt="" class="w-4 h-4 mx-2">
|
||||
<span class="">stars</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<highlightjs class="snippet-container" :code="content"/>
|
||||
<div :id="'comment' + gist.id" class="flex hidden justify-between text-menu-text font-fira_retina mt-4 pt-4 border-top">
|
||||
<p id="comment" v-if="comment" class="w-5/6">{{ comment }}</p>
|
||||
<p v-else class="w-5/6">No comments.</p>
|
||||
<img src="/icons/close.svg" alt="" class="hover:cursor-pointer" @click="showComment(gist.id)">
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.snippet-container {
|
||||
background-color: #011221;
|
||||
padding: 5px;
|
||||
border-radius: 15px;
|
||||
border: 1px solid #1E2D3D;
|
||||
font-size: 12px;
|
||||
overflow-y: scroll;
|
||||
overflow-x: scroll;
|
||||
max-height: 220px;
|
||||
}
|
||||
|
||||
.snippet-container pre {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
max-height: 220px;
|
||||
}
|
||||
|
||||
.snippet-container code {
|
||||
white-space: pre-wrap;
|
||||
max-height: 220px;
|
||||
width: max-content;
|
||||
overflow: hidden;
|
||||
|
||||
}
|
||||
|
||||
.snippet-container::-webkit-scrollbar {
|
||||
display: none; /* Safari and Chrome */
|
||||
}
|
||||
|
||||
pre code.hljs{
|
||||
display:block;
|
||||
/* overflow-x:auto; */
|
||||
padding:1.5em
|
||||
}
|
||||
|
||||
code.hljs{
|
||||
padding:3px 5px
|
||||
}
|
||||
|
||||
#comment {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
#username:hover {
|
||||
color: #5e6ef2;
|
||||
}
|
||||
|
||||
/* #comment {
|
||||
|
||||
} */
|
||||
|
||||
.hljs{color:#607B96;background:#011221}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#ff7b72}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#d2a8ff}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#79c0ff}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#a5d6ff}.hljs-built_in,.hljs-symbol{color:#ffa657}.hljs-code,.hljs-comment,.hljs-formula{color:#8b949e}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#7ee787}.hljs-subst{color:#c9d1d9}.hljs-section{color:#1f6feb;font-weight:700}.hljs-bullet{color:#f2cc60}.hljs-emphasis{color:#c9d1d9;font-style:italic}.hljs-strong{color:#c9d1d9;font-weight:700}.hljs-addition{color:#aff5b4;background-color:#033a16}.hljs-deletion{color:#ffdcd7;background-color:#67060c}
|
||||
|
||||
</style>
|
||||
|
||||
<script>
|
||||
|
||||
import hljsVuePlugin from "@highlightjs/vue-plugin";
|
||||
import 'highlight.js/lib/common';
|
||||
|
||||
export default {
|
||||
name: 'GistSnippet',
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
gist: null,
|
||||
monthsAgo: null,
|
||||
content: null,
|
||||
language: null,
|
||||
dataFetched: false,
|
||||
comment: null
|
||||
}
|
||||
},
|
||||
mounted(){
|
||||
fetch(`https://api.github.com/gists/${this.id}`)
|
||||
.then(response => response.json())
|
||||
.then(data => this.setValues(data))
|
||||
|
||||
},
|
||||
methods: {
|
||||
async setValues(gist) {
|
||||
this.gist = gist
|
||||
this.monthsAgo = this.setMonths(gist.created_at)
|
||||
this.content = this.setSnippet(gist)
|
||||
this.language = Object.values(gist.files)[0].language
|
||||
this.dataFetched = true
|
||||
this.comment = await this.setComments(gist.comments_url)
|
||||
},
|
||||
setMonths(date) {
|
||||
let now = new Date()
|
||||
let gistDate = new Date(date)
|
||||
let diff = now.getTime() - gistDate.getTime()
|
||||
let days = Math.floor(diff / (1000 * 3600 * 24))
|
||||
let months = Math.floor(days / 30)
|
||||
return months
|
||||
},
|
||||
setSnippet(gist) {
|
||||
let snippet = Object.values(gist.files)[0].content // Object.values(gist.files)[0].filename.content
|
||||
return snippet
|
||||
},
|
||||
async setComments(comments_url){
|
||||
let response = await fetch(comments_url)
|
||||
let data = await response.json()
|
||||
try{
|
||||
let body = data[0].body
|
||||
return body
|
||||
} catch {
|
||||
console.log(`no comments found on ${comments_url}`)
|
||||
}
|
||||
},
|
||||
showComment(id) {
|
||||
let comment = document.getElementById('comment' + id)
|
||||
comment.classList.toggle('hidden')
|
||||
}
|
||||
},
|
||||
components: {
|
||||
highlightjs: hljsVuePlugin.component
|
||||
}
|
||||
}
|
||||
</script>
|
64
components/GithubCorner.vue
Normal file
64
components/GithubCorner.vue
Normal file
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<a :href="url" class="github-corner" target="_blank" aria-label="View source on Github">
|
||||
<svg width="80" height="80" viewBox="0 0 250 250" aria-hidden="true">
|
||||
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
|
||||
<path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
|
||||
fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path>
|
||||
<path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
|
||||
fill="currentColor" class="octo-body"></path>
|
||||
</svg>
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'GithubCorner',
|
||||
props: {
|
||||
url: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* ----------------------------------------------
|
||||
* GitHub Corners
|
||||
* w: https://github.com/tholman/github-corners
|
||||
* ---------------------------------------------- */
|
||||
|
||||
.github-corner {
|
||||
fill: #071511;
|
||||
color: #43D9AD;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
border: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.github-corner:hover .octo-arm {
|
||||
animation: octocat-wave 560ms ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes octocat-wave {
|
||||
0%,
|
||||
100% {
|
||||
transform: rotate(0);
|
||||
}
|
||||
20%,
|
||||
60% {
|
||||
transform: rotate(-25deg);
|
||||
}
|
||||
40%,
|
||||
80% {
|
||||
transform: rotate(10deg);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.github-corner {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
98
components/MobileMenu.vue
Normal file
98
components/MobileMenu.vue
Normal file
@@ -0,0 +1,98 @@
|
||||
<template>
|
||||
<div id="mobile-menu" class="w-full z-10 lg:hidden">
|
||||
|
||||
<!-- header -->
|
||||
<div id="mobile-header" class="w-full h-16 flex justify-between items-center">
|
||||
<NuxtLink class="text-menu-text font-fira_retina flex h-full items-center mx-5" to="/" @click="goHome()">
|
||||
{{ config.dev.logo_name }}
|
||||
</NuxtLink>
|
||||
<img src="/icons/burger.svg" v-if="!menuOpen" @click="toggleMobileMenu()" class="w-5 h-5 mx-5 my-auto"/>
|
||||
<img src="/icons/burger-close.svg" v-else @click="toggleMobileMenu()" name="icon-park-outline:close" class="w-5 h-5 mx-5 my-auto"/>
|
||||
</div>
|
||||
|
||||
<!-- mobile menu -->
|
||||
<div id="menu" class="bg-mobile-menu-blue z-10 hidden">
|
||||
<NuxtLink id="nav-link-mobile" to="/" :class="{ active: isActive('/') }" @click="toggleMobileMenu()">
|
||||
_hello
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink id="nav-link-mobile" to="/about-me" :class="{ active: isActive('/about-me') }" @click="toggleMobileMenu()">
|
||||
_about-me
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink id="nav-link-mobile" to="/projects" :class="{ active: isActive('/projects') }" @click="toggleMobileMenu()">
|
||||
_projects
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink id="nav-link-mobile" to="/contact-me" :class="{ active: isActive('/contact-me') }" @click="toggleMobileMenu()">
|
||||
_contact-me
|
||||
</NuxtLink>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data(){
|
||||
return {
|
||||
menuOpen: false
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
const config = useRuntimeConfig()
|
||||
|
||||
return {
|
||||
config
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleMobileMenu(){
|
||||
|
||||
this.menuOpen ? this.menuOpen = false : this.menuOpen = true
|
||||
|
||||
const menu = document.getElementById('menu');
|
||||
menu.classList.toggle('hidden')
|
||||
|
||||
const page = document.getElementsByTagName('main')[0];
|
||||
// Hide / show section
|
||||
if (page.style.display === 'none') {
|
||||
page.style.display = 'flex';
|
||||
} else {
|
||||
page.style.display = 'none';
|
||||
}
|
||||
},
|
||||
goHome() {
|
||||
const menu = document.getElementById('menu');
|
||||
if(!menu.classList.contains('hidden')){
|
||||
menu.classList.toggle('hidden')
|
||||
document.getElementsByTagName('main')[0].style.display = 'flex';
|
||||
this.menuOpen ? this.menuOpen = false : this.menuOpen = true
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// Set active class to current page link
|
||||
isActive() {
|
||||
return route => this.$route.path === route;
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
#mobile-header {
|
||||
border-bottom: 1px solid #1E2D3D;
|
||||
}
|
||||
|
||||
#nav-link-mobile {
|
||||
border-bottom: 1px solid #1E2D3D;
|
||||
@apply text-menu-text font-fira_retina px-6 py-4 flex items-center;
|
||||
}
|
||||
|
||||
#nav-link-mobile.active {
|
||||
color: white
|
||||
}
|
||||
|
||||
</style>
|
80
components/ProjectCard.vue
Normal file
80
components/ProjectCard.vue
Normal file
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<div id="project" :key="key" class="lg:mx-5">
|
||||
|
||||
<span class="flex text-sm my-3">
|
||||
<h3 v-if="index == null" class="text-purplefy font-fira_bold mr-3">Project {{ key + 1 }}</h3>
|
||||
<h3 v-else class="text-purplefy font-fira_bold mr-3">Project {{ index + 1 }}</h3>
|
||||
<h4 class="font-fira_retina text-menu-text"> // {{ project.title }}</h4>
|
||||
</span>
|
||||
|
||||
<div id="project-card" class="flex flex-col">
|
||||
<div id="window">
|
||||
<div class="absolute flex right-3 top-3">
|
||||
<img v-for="tech in project.tech" :key="tech" :src="'/icons/techs/filled/' + tech + '.svg'" alt="" class="w-6 h-6 mx-1 hover:opacity-75">
|
||||
</div>
|
||||
<img id="showcase" :src="project.img" alt="" class="">
|
||||
</div>
|
||||
|
||||
<div class="pb-8 pt-6 px-6 border-top">
|
||||
<p class="text-menu-text font-fira_retina text-sm mb-5">
|
||||
{{ project.description }}
|
||||
</p>
|
||||
<a id="view-button" :href="project.url" target="_blank" class="text-white font-fira_retina py-2 px-4 w-fit text-xs rounded-lg">
|
||||
view-project
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const { project, key, index } = defineProps(['project', 'key', 'index'])
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#project {
|
||||
min-width: 400px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
#project-card {
|
||||
border: 1px solid #1E2D3D;
|
||||
background-color: #011221;
|
||||
border-radius: 15px;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
#window {
|
||||
max-height: 120px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#showcase {
|
||||
border-top-right-radius: 15px;
|
||||
border-top-left-radius: 15px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
#project {
|
||||
min-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
#project {
|
||||
width: 100%;
|
||||
min-width: 100%;
|
||||
padding-inline: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1350px) {
|
||||
#project {
|
||||
width: 100%;
|
||||
min-width: 100%;
|
||||
padding-inline: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
24
components/README.md
Normal file
24
components/README.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# `components/` [Directory](https://nuxt.com/docs/getting-started/views#components)
|
||||
|
||||
Most components are reusable pieces of the user interface, like buttons and menus. In Nuxt, you can create these components in the `components/` directory, and they will be automatically available across your application without having to explicitly import them.
|
||||
|
||||
*app.vue*
|
||||
````html
|
||||
<template>
|
||||
<div>
|
||||
<h1>Welcome to the homepage</h1>
|
||||
<AppAlert>
|
||||
This is an auto-imported component.
|
||||
</AppAlert>
|
||||
</div>
|
||||
</template>
|
||||
````
|
||||
|
||||
*components/AppAlert.vue*
|
||||
````html
|
||||
<template>
|
||||
<span>
|
||||
<slot />
|
||||
</span>
|
||||
</template>
|
||||
````
|
580
components/SnakeGame.vue
Normal file
580
components/SnakeGame.vue
Normal file
@@ -0,0 +1,580 @@
|
||||
<template>
|
||||
<div id="console">
|
||||
|
||||
<!-- bolts -->
|
||||
<img id="corner" src="/icons/console/bolt-up-left.svg" alt="" class="absolute top-2 left-2 opacity-70">
|
||||
<img id="corner" src="/icons/console/bolt-up-right.svg" alt="" class="absolute top-2 right-2 opacity-70">
|
||||
<img id="corner" src="/icons/console/bolt-down-left.svg" alt="" class="absolute bottom-2 left-2 opacity-70">
|
||||
<img id="corner" src="/icons/console/bolt-down-right.svg" alt="" class="absolute bottom-2 right-2 opacity-70">
|
||||
|
||||
|
||||
<!-- Game Screen -->
|
||||
<div id="game-screen" ref="gameScreen"></div>
|
||||
|
||||
<button id="start-button" class="font-fira_retina" @click="startGame">start-game</button>
|
||||
|
||||
<!-- Game Over -->
|
||||
<div id="game-over" class="hidden">
|
||||
<span class="font-fira_retina text-greenfy bg-bluefy-dark h-12 flex items-center justify-center">GAME OVER!</span>
|
||||
<button class="font-fira_retina text-menu-text text-sm flex items-center justify-center w-full py-6 hover:text-white" @click="startAgain">start-again</button>
|
||||
</div>
|
||||
|
||||
<div id="congrats" class="hidden">
|
||||
<span class="font-fira_retina text-greenfy bg-bluefy-dark h-12 flex items-center justify-center">WELL DONE!</span>
|
||||
<button class="font-fira_retina text-menu-text text-sm flex items-center justify-center w-full py-6 hover:text-white" @click="startAgain">play-again</button>
|
||||
</div>
|
||||
|
||||
<div id="console-menu" class="h-full flex flex-col items-end justify-between">
|
||||
|
||||
<div>
|
||||
|
||||
<div id="instructions" class="font-fira_retina text-sm text-white">
|
||||
<p>// use your keyboard</p>
|
||||
<p>// arrows to play</p>
|
||||
|
||||
<div id="buttons" class="w-full flex flex-col items-center gap-1 pt-5">
|
||||
|
||||
<button id="console-button" class="button-up" @click="move('up')">
|
||||
<img src="/icons/console/arrow-button.svg" alt="">
|
||||
</button>
|
||||
|
||||
<div class="grid grid-cols-3 gap-1">
|
||||
<button id="console-button" class="button-left" @click="move('left')">
|
||||
<img src="/icons/console/arrow-button.svg" alt="" class="-rotate-90">
|
||||
</button>
|
||||
|
||||
<button id="console-button" class="button-down" @click="move('down')">
|
||||
<img src="/icons/console/arrow-button.svg" alt="" class="rotate-180">
|
||||
</button>
|
||||
|
||||
<button id="console-button" class="button-right" @click="move('right')">
|
||||
<img src="/icons/console/arrow-button.svg" alt="" class="rotate-90">
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- score board -->
|
||||
<div id="score-board" class="w-full flex flex-col pl-5">
|
||||
<p class="font-fira_retina text-white pt-5">// food left</p>
|
||||
|
||||
<div id="score" class="grid grid-cols-5 gap-5 justify-items-center pt-5 w-fit">
|
||||
<div class="food"></div>
|
||||
<div class="food"></div>
|
||||
<div class="food"></div>
|
||||
<div class="food"></div>
|
||||
<div class="food"></div>
|
||||
<div class="food"></div>
|
||||
<div class="food"></div>
|
||||
<div class="food"></div>
|
||||
<div class="food"></div>
|
||||
<div class="food"></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- skip -->
|
||||
<NuxtLink id="skip-btn" to="/about-me" class="font-fira_retina flex hover:bg-white/20">
|
||||
skip
|
||||
</NuxtLink>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
score: 0,
|
||||
gameInterval: null,
|
||||
gameStarted: false,
|
||||
gameOver: false,
|
||||
food: { x: 10, y: 5 },
|
||||
snake: [
|
||||
{ x: 10, y: 12 },
|
||||
{ x: 10, y: 13 },
|
||||
{ x: 10, y: 14 },
|
||||
{ x: 10, y: 15 },
|
||||
{ x: 10, y: 16 },
|
||||
{ x: 10, y: 17 },
|
||||
{ x: 10, y: 18 },
|
||||
{ x: 11, y: 18 },
|
||||
{ x: 12, y: 18 },
|
||||
{ x: 13, y: 18 },
|
||||
{ x: 14, y: 18 },
|
||||
{ x: 15, y: 18 },
|
||||
{ x: 15, y: 19 },
|
||||
{ x: 15, y: 20 },
|
||||
{ x: 15, y: 21 },
|
||||
{ x: 15, y: 22 },
|
||||
{ x: 15, y: 23 },
|
||||
{ x: 15, y: 24 },
|
||||
],
|
||||
direction: "up",
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
startGame() {
|
||||
|
||||
// hide start button
|
||||
document.getElementById("start-button").style.display = "none";
|
||||
|
||||
// start game
|
||||
this.gameStarted = true;
|
||||
this.gameInterval = setInterval(this.moveSnake, 50);
|
||||
},
|
||||
startAgain() {
|
||||
// Mostrar botón de start-game
|
||||
document.getElementById("start-button").style.display = "block";
|
||||
|
||||
// Ocultar game over
|
||||
document.getElementById("game-over").style.display = "none";
|
||||
document.getElementById("congrats").style.display = "none";
|
||||
|
||||
|
||||
// reiniciar datos del juego
|
||||
this.gameStarted = false;
|
||||
this.gameOver = false;
|
||||
this.restartScore();
|
||||
this.food = {
|
||||
x: 10,
|
||||
y: 5
|
||||
};
|
||||
this.snake = [
|
||||
{ x: 10, y: 12 },
|
||||
{ x: 10, y: 13 },
|
||||
{ x: 10, y: 14 },
|
||||
{ x: 10, y: 15 },
|
||||
{ x: 10, y: 16 },
|
||||
{ x: 10, y: 17 },
|
||||
{ x: 10, y: 18 },
|
||||
{ x: 11, y: 18 },
|
||||
{ x: 12, y: 18 },
|
||||
{ x: 13, y: 18 },
|
||||
{ x: 14, y: 18 },
|
||||
{ x: 15, y: 18 },
|
||||
{ x: 15, y: 19 },
|
||||
{ x: 15, y: 20 },
|
||||
{ x: 15, y: 21 },
|
||||
{ x: 15, y: 22 },
|
||||
{ x: 15, y: 23 },
|
||||
{ x: 15, y: 24 },
|
||||
],
|
||||
this.direction = "up";
|
||||
|
||||
// limpiar intervalo de juego
|
||||
clearInterval(this.gameInterval);
|
||||
this.render();
|
||||
},
|
||||
// ... resto del código
|
||||
moveSnake() {
|
||||
let newX = this.snake[0].x;
|
||||
let newY = this.snake[0].y;
|
||||
|
||||
switch (this.direction) {
|
||||
case "up":
|
||||
newY--;
|
||||
break;
|
||||
case "down":
|
||||
newY++;
|
||||
break;
|
||||
case "left":
|
||||
newX--;
|
||||
break;
|
||||
case "right":
|
||||
newX++;
|
||||
break;
|
||||
}
|
||||
|
||||
// check if snake dont leave from game window
|
||||
// and check if snake dont eat itself
|
||||
if (
|
||||
newX >= 0 &&
|
||||
newX < 24 &&
|
||||
newY >= 0 &&
|
||||
newY < 40 &&
|
||||
!this.snake.find(
|
||||
snakeCell => snakeCell.x === newX && snakeCell.y === newY
|
||||
)
|
||||
) {
|
||||
/* snake move next cell */
|
||||
this.snake.unshift({ x: newX, y: newY });
|
||||
|
||||
/* check snake next cell is food */
|
||||
if (newX === this.food.x && newY === this.food.y) {
|
||||
|
||||
// add score
|
||||
this.score++;
|
||||
|
||||
// add food to score board
|
||||
const scoreFoods = document.getElementsByClassName("food");
|
||||
scoreFoods[this.score - 1].style.opacity = 1;
|
||||
|
||||
// check if score is 10 (max score)
|
||||
if(this.score === 10) {
|
||||
|
||||
// move snake head to food (fix snake head position at end)
|
||||
this.snake.unshift({ x: newX, y: newY }); // move head
|
||||
this.food = { x: null, y: null } // remove food
|
||||
clearInterval(this.gameInterval); // stop game
|
||||
document.getElementById('congrats').style.display = 'block' // show congrats
|
||||
this.gameOver = true; // game over
|
||||
this.gameStarted = false; // stop game
|
||||
|
||||
} else {
|
||||
|
||||
// create new food
|
||||
this.food = {
|
||||
x: Math.floor(Math.random() * 24),
|
||||
y: Math.floor(Math.random() * 40)
|
||||
};
|
||||
}
|
||||
|
||||
} else {
|
||||
// if next cell is not food: snake pop last cell
|
||||
this.snake.pop();
|
||||
}
|
||||
} else {
|
||||
// GAME OVER: if snake leave from game window or eat itself
|
||||
clearInterval(this.gameInterval);
|
||||
document.getElementById('game-over').style.display = 'block'
|
||||
this.gameStarted = false;
|
||||
this.gameOver = true;
|
||||
}
|
||||
this.render();
|
||||
},
|
||||
render() {
|
||||
let gameScreen = this.$refs.gameScreen;
|
||||
gameScreen.innerHTML = "";
|
||||
|
||||
// responsive cell screen
|
||||
// (this.$refs.gameScreen.offsetWidth / 20) + "px";
|
||||
|
||||
/* const widthCells = window.innerWidth > 1536 ? 24 : 20; */
|
||||
const cellSize = window.innerWidth > 1536 ? "10px" : "8px";
|
||||
// eje y
|
||||
for (let i = 0; i < 40; i++) {
|
||||
// exe x
|
||||
for (let j = 0; j < 24; j++) {
|
||||
|
||||
/* cell style */
|
||||
let cell = document.createElement("div");
|
||||
cell.classList.add("cell");
|
||||
cell.style.width = cellSize
|
||||
cell.style.height = cellSize
|
||||
cell.style.display = "flex";
|
||||
cell.style.flexShrink = 0;
|
||||
cell.classList.add("black");
|
||||
|
||||
/* Food cell style */
|
||||
if (j === this.food.x && i === this.food.y) {
|
||||
cell.style.backgroundColor = "#43D9AD";
|
||||
cell.style.borderRadius = "50%";
|
||||
cell.style.boxShadow = "0 0 10px #43D9AD";
|
||||
}
|
||||
|
||||
/* Estilo de la serpiente a medida que va crediendo */
|
||||
let snakeCell = this.snake.find(
|
||||
snakeCell => snakeCell.x === j && snakeCell.y === i
|
||||
);
|
||||
|
||||
if (snakeCell) {
|
||||
cell.style.backgroundColor = "#43D9AD";
|
||||
cell.style.opacity = 1 - (this.snake.indexOf(snakeCell) / this.snake.length);
|
||||
cell.classList.add("green");
|
||||
|
||||
}
|
||||
|
||||
/* Estilo de la cabeza */
|
||||
if (snakeCell && this.snake.indexOf(snakeCell) === 0) {
|
||||
|
||||
let headRadius = "5px";
|
||||
if (this.direction === "up") {
|
||||
cell.style.borderTopLeftRadius = headRadius;
|
||||
cell.style.borderTopRightRadius = headRadius;
|
||||
}
|
||||
if (this.direction === "down") {
|
||||
|
||||
cell.style.borderBottomLeftRadius = headRadius;
|
||||
cell.style.borderBottomRightRadius = headRadius;
|
||||
}
|
||||
if (this.direction === "left") {
|
||||
cell.style.borderTopLeftRadius = headRadius;
|
||||
cell.style.borderBottomLeftRadius = headRadius;
|
||||
}
|
||||
if (this.direction === "right") {
|
||||
cell.style.borderTopRightRadius = headRadius;
|
||||
cell.style.borderBottomRightRadius = headRadius;
|
||||
}
|
||||
}
|
||||
gameScreen.appendChild(cell);
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
restartScore(){
|
||||
this.score = 0;
|
||||
const scoreFoods = document.getElementsByClassName("food");
|
||||
for (let i = 0; i < scoreFoods.length; i++) {
|
||||
scoreFoods[i].style.opacity = 0.3;
|
||||
}
|
||||
},
|
||||
move(direction){
|
||||
switch (direction) {
|
||||
case "up":
|
||||
if (this.direction !== "down") {
|
||||
this.direction = "up";
|
||||
}
|
||||
break;
|
||||
case "down":
|
||||
if (this.direction !== "up") {
|
||||
this.direction = "down";
|
||||
}
|
||||
break;
|
||||
case "left":
|
||||
if (this.direction !== "right") {
|
||||
this.direction = "left";
|
||||
}
|
||||
break;
|
||||
case "right":
|
||||
if (this.direction !== "left") {
|
||||
this.direction = "right";
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener("keydown", event => {
|
||||
if (this.gameStarted) {
|
||||
switch (event.keyCode) {
|
||||
case 37:
|
||||
if (this.direction !== "right") {
|
||||
this.direction = "left";
|
||||
}
|
||||
break;
|
||||
case 38:
|
||||
if (this.direction !== "down") {
|
||||
this.direction = "up";
|
||||
}
|
||||
break;
|
||||
case 39:
|
||||
if (this.direction !== "left") {
|
||||
this.direction = "right";
|
||||
}
|
||||
break;
|
||||
case 40:
|
||||
if (this.direction !== "up") {
|
||||
this.direction = "down";
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (event.keyCode) {
|
||||
case 32:
|
||||
if(this.gameOver){
|
||||
this.startAgain();
|
||||
}else {
|
||||
this.startGame();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/* window.innerWidth < 1536 ? cellSize = 8 : cellSize = 10; */
|
||||
/* this.food = {
|
||||
x: Math.floor(Math.random() * 24),
|
||||
y: Math.floor(Math.random() * 40)
|
||||
}; */
|
||||
window.onresize = () => {
|
||||
this.render();
|
||||
};
|
||||
|
||||
this.render();
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#console {
|
||||
width: 530px;
|
||||
height: 475px;
|
||||
border: 1px solid black;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: linear-gradient(to bottom, rgba(35, 123, 109, 1), rgba(67, 217, 173, 0.13));
|
||||
border-radius: 10px;
|
||||
padding: 30px;
|
||||
position: relative;
|
||||
|
||||
}
|
||||
|
||||
#game-screen {
|
||||
width: 240px;
|
||||
height: 400px;
|
||||
border-radius: 10px;
|
||||
background-color: rgba(1, 22, 39, 0.84);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
box-shadow: inset 0 0 10px #00000071;
|
||||
}
|
||||
|
||||
#start-button {
|
||||
padding-inline: 16px;
|
||||
padding-block: 8px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid black;
|
||||
background-color: #FEA55F;
|
||||
color: black;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
bottom: 20%;
|
||||
left: 17%;
|
||||
font-size: 0.875rem; /* 14px */
|
||||
line-height: 1.25rem; /* 20px */
|
||||
}
|
||||
|
||||
#start-button:hover {
|
||||
background-color: rgb(255, 178, 119);
|
||||
}
|
||||
|
||||
#console-menu{
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
#console-button {
|
||||
background-color: #010C15;
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 50px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
#console-button:hover {
|
||||
background-color: #010c15d8;
|
||||
box-shadow: #43D9AD 0 0 10px;
|
||||
}
|
||||
|
||||
#instructions {
|
||||
background-color: rgba(1, 20, 35, 0.19);
|
||||
border-radius: 7px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.food {
|
||||
background-color: #43D9AD;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 10px #43D9AD;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
#game-over, #congrats {
|
||||
position: absolute;
|
||||
bottom: 12%;
|
||||
color: #43D9AD;
|
||||
width: 240px;
|
||||
}
|
||||
|
||||
#game-over, #congrats > span {
|
||||
font-size: 1.5rem; /* 24px */
|
||||
line-height: 2rem; /* 32px */
|
||||
}
|
||||
|
||||
#corner {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
#skip-btn{
|
||||
font-size: 14px;
|
||||
color: white;
|
||||
padding-inline: 16px;
|
||||
padding-block: 8px;
|
||||
border: 2px solid white;
|
||||
border-radius: 0.5rem; /* 8px */
|
||||
}
|
||||
|
||||
|
||||
@media (min-width: 1024px) and (max-width: 1536px) {
|
||||
#game-screen {
|
||||
width: 192px;
|
||||
height: 320px;
|
||||
}
|
||||
|
||||
#console {
|
||||
width: 420px;
|
||||
height: 370px;
|
||||
padding: 24px;
|
||||
|
||||
}
|
||||
|
||||
#start-button {
|
||||
padding-inline: 12px;
|
||||
padding-block: 6px;
|
||||
border-radius: 8px;
|
||||
bottom: 20%;
|
||||
left: 17%;
|
||||
font-size: 0.75rem; /* 14px */
|
||||
line-height: 1rem; /* 20px */
|
||||
}
|
||||
|
||||
#console-menu{
|
||||
height: 320px;
|
||||
}
|
||||
|
||||
#instructions {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#console-button {
|
||||
width: 40px;
|
||||
height: 25px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
#score-board {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.food {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
#game-over, #congrats {
|
||||
position: absolute;
|
||||
bottom: 10%;
|
||||
color: #43D9AD;
|
||||
width: 192px;
|
||||
}
|
||||
|
||||
#game-over, #congrats > span {
|
||||
font-size: 1.125rem; /* 18px */
|
||||
line-height: 1.75rem; /* 28px */
|
||||
}
|
||||
|
||||
#corner {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
#skip-btn{
|
||||
font-size: 12px;
|
||||
padding-inline: 12px;
|
||||
padding-block: 6px;
|
||||
border: 2px solid white;
|
||||
border-radius: 0.5rem; /* 8px */
|
||||
}
|
||||
}
|
||||
</style>
|
Reference in New Issue
Block a user