Browse Source

feat: fancy word cloud

main
nicolas.marsal 4 years ago
parent
commit
d518724d46
No known key found for this signature in database
GPG Key ID: 268AB819B6453541
  1. 54
      package-lock.json
  2. 2
      package.json
  3. 47
      src/App.vue
  4. 92
      src/components/common/WordCloud.vue
  5. 11
      src/components/informations/WordsCloud.vue
  6. 38
      src/data/skills.ts

54
package-lock.json generated

@ -13,6 +13,8 @@
"@fortawesome/free-regular-svg-icons": "^6.1.1",
"@fortawesome/free-solid-svg-icons": "^6.1.1",
"@fortawesome/vue-fontawesome": "^3.0.1",
"@types/d3-cloud": "^1.2.5",
"d3-cloud": "^1.2.5",
"pinia": "^2.0.14",
"vue": "^3.2.37",
"vue-i18n": "^9.1.10"
@ -783,6 +785,19 @@
"integrity": "sha512-LwzQKA4vzIct1zNZzBmRKI9QuNpLgTQMEjsQLf3BXuGYb3QPTP4Yjf6mkdX+X1mYttZ808QpOwAzZjv28kq7DA==",
"dev": true
},
"node_modules/@types/d3": {
"version": "3.5.47",
"resolved": "https://registry.npmjs.org/@types/d3/-/d3-3.5.47.tgz",
"integrity": "sha512-VkWIQoZXLFdcBGe5pdBKJmTU3fmpXvo/KV6ixvTzOMl1yJ2hbTXpfvsziag0kcaerPDwas2T0vxojwQG3YwivQ=="
},
"node_modules/@types/d3-cloud": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/@types/d3-cloud/-/d3-cloud-1.2.5.tgz",
"integrity": "sha512-vEIER9DsEBUOdpRiwCh3n1qE+cV6h4e1LhxhY2sLt+m8LPNAIkOOhTlqk0JDiBwD+ZPM8ynFAOU3AuPuVYBFBA==",
"dependencies": {
"@types/d3": "^3"
}
},
"node_modules/@types/json-schema": {
"version": "7.0.11",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
@ -1522,6 +1537,19 @@
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.20.tgz",
"integrity": "sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA=="
},
"node_modules/d3-cloud": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/d3-cloud/-/d3-cloud-1.2.5.tgz",
"integrity": "sha512-4s2hXZgvs0CoUIw31oBAGrHt9Kt/7P9Ik5HIVzISFiWkD0Ga2VLAuO/emO/z1tYIpE7KG2smB4PhMPfFMJpahw==",
"dependencies": {
"d3-dispatch": "^1.0.3"
}
},
"node_modules/d3-dispatch": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.6.tgz",
"integrity": "sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA=="
},
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@ -4949,6 +4977,19 @@
"integrity": "sha512-LwzQKA4vzIct1zNZzBmRKI9QuNpLgTQMEjsQLf3BXuGYb3QPTP4Yjf6mkdX+X1mYttZ808QpOwAzZjv28kq7DA==",
"dev": true
},
"@types/d3": {
"version": "3.5.47",
"resolved": "https://registry.npmjs.org/@types/d3/-/d3-3.5.47.tgz",
"integrity": "sha512-VkWIQoZXLFdcBGe5pdBKJmTU3fmpXvo/KV6ixvTzOMl1yJ2hbTXpfvsziag0kcaerPDwas2T0vxojwQG3YwivQ=="
},
"@types/d3-cloud": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/@types/d3-cloud/-/d3-cloud-1.2.5.tgz",
"integrity": "sha512-vEIER9DsEBUOdpRiwCh3n1qE+cV6h4e1LhxhY2sLt+m8LPNAIkOOhTlqk0JDiBwD+ZPM8ynFAOU3AuPuVYBFBA==",
"requires": {
"@types/d3": "^3"
}
},
"@types/json-schema": {
"version": "7.0.11",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
@ -5481,6 +5522,19 @@
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.20.tgz",
"integrity": "sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA=="
},
"d3-cloud": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/d3-cloud/-/d3-cloud-1.2.5.tgz",
"integrity": "sha512-4s2hXZgvs0CoUIw31oBAGrHt9Kt/7P9Ik5HIVzISFiWkD0Ga2VLAuO/emO/z1tYIpE7KG2smB4PhMPfFMJpahw==",
"requires": {
"d3-dispatch": "^1.0.3"
}
},
"d3-dispatch": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.6.tgz",
"integrity": "sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA=="
},
"debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",

2
package.json

@ -15,6 +15,8 @@
"@fortawesome/free-regular-svg-icons": "^6.1.1",
"@fortawesome/free-solid-svg-icons": "^6.1.1",
"@fortawesome/vue-fontawesome": "^3.0.1",
"@types/d3-cloud": "^1.2.5",
"d3-cloud": "^1.2.5",
"pinia": "^2.0.14",
"vue": "^3.2.37",
"vue-i18n": "^9.1.10"

47
src/App.vue

@ -6,12 +6,23 @@ import MyLanguages from "@/components/informations/MyLanguages.vue";
import MyHobbies from "@/components/informations/MyHobbies.vue";
import MySkills from "@/components/skills/MySkills.vue";
import MyExperiences from "@/components/experiences/MyExperiences.vue";
import WordCloud from "@/components/common/WordCloud.vue";
</script>
<template>
<div class="main">
<div class="left">
<FirstNameAndLastName />
<div
style="
display: flex;
justify-content: center;
align-items: center;
width: 100%;
"
>
<WordCloud :width="290" :height="200"></WordCloud>
</div>
<MyInformations />
<MyEducation />
<MyLanguages />
@ -24,6 +35,42 @@ import MyExperiences from "@/components/experiences/MyExperiences.vue";
</div>
</template>
<script lang="ts">
import {
Ansible,
Git,
Helm,
Javascript,
Jenkins,
Kubernetes,
Skaffold,
Typescript,
} from "@/data/skills";
export default {
name: "app",
methods: {
wordClickHandler(name, value, vm) {
console.log("wordClickHandler", name, value, vm);
},
},
data() {
return {
myColors: ["#1f77b4", "#629fc9", "#94bedb", "#c9e0ef"],
defaultWords: [
Kubernetes,
Skaffold,
Ansible,
Helm,
Javascript,
Typescript,
Git,
Jenkins,
].map((s) => ({ name: s.name, value: s.score })),
};
},
};
</script>
<style scoped>
.main {
display: flex;

92
src/components/common/WordCloud.vue

@ -0,0 +1,92 @@
<template>
<svg :width="width" :height="height">
<g :transform="`translate(${width / 2}, ${height / 2})`">
<text
v-for="word in words"
:key="word.text"
:style="{
fontSize: word.size + 'px',
fontFamily: word.font,
}"
:transform="`translate(${word.x}, ${word.y})rotate(${word.rotate})`"
text-anchor="middle"
@click="highlightText(word.text)"
>
{{ word.text }}
</text>
</g>
</svg>
</template>
<script lang="ts">
import cloud from "d3-cloud";
import * as skills from "@/data/skills";
import { defineComponent } from "vue";
export default defineComponent({
props: {
width: Number,
height: Number,
},
data() {
return {
loading: false,
words: [] as cloud.Word[],
minFontSize: 8,
maxFontSize: 40,
};
},
created() {
this.computeWords();
},
methods: {
highlightText(text: string) {
console.log(text);
},
computeWords() {
this.loading = true;
const words = Object.keys(skills)
.map((p) => skills[p])
.map((s) => ({ text: s.name, size: s.score }));
const min = words
.map((s) => s.size)
.reduce((prev, current) => Math.min(prev, current));
const max = words
.map((s) => s.size)
.reduce((prev, current) => Math.max(prev, current));
const scaleFontSize = (size: number) => {
const number =
((size - min) / (max - min)) * (this.maxFontSize - this.minFontSize) +
this.minFontSize;
return number;
};
const randomRotate = (s: number) => {
const range = 120 * (1 - ((s - min) / (max - min)) * 0.7);
return Math.random() * range - range / 2;
};
cloud<{ size: number } & cloud.Word>()
.size([this.width!, this.height!])
.words(words)
.padding(1)
.rotate((w) => randomRotate(w.size))
.spiral("archimedean")
.fontSize((d) => scaleFontSize(d.size))
.on("end", (d: cloud.Word[]) => {
console.log(
`d: ${d.length} - w: ${words.length} - f: ${this.maxFontSize}`
);
if (d.length < words.length) {
console.log("On recommence !");
this.maxFontSize = this.maxFontSize - 2;
this.computeWords();
} else {
this.words = d;
this.loading = false;
}
})
.start();
},
},
});
</script>

11
src/components/informations/WordsCloud.vue

@ -1,11 +0,0 @@
<template>
<div>Words Cloud !</div>
</template>
<script>
export default {
name: "WordsCloud",
};
</script>
<style scoped></style>

38
src/data/skills.ts

@ -4,51 +4,51 @@ export const DevOps: Skill = { name: "DevOps", score: 0 };
export const Docker: Skill = { name: "Docker", score: 0, parent: [DevOps] };
export const Kubernetes: Skill = {
name: "Kubernetes",
score: 0,
score: 28,
parent: [DevOps],
};
export const Skaffold: Skill = {
name: "Skaffold",
score: 0,
score: 24,
parent: [Kubernetes],
};
export const Ansible: Skill = { name: "Ansible", score: 0, parent: [DevOps] };
export const Helm: Skill = { name: "Helm", score: 0, parent: [Kubernetes] };
export const Ansible: Skill = { name: "Ansible", score: 17, parent: [DevOps] };
export const Helm: Skill = { name: "Helm", score: 14, parent: [Kubernetes] };
export const UX: Skill = { name: "UX", score: 0 };
export const UX: Skill = { name: "UX", score: 15 };
export const CI_CD: Skill = { name: "CI/CD", score: 0 };
export const Maven: Skill = { name: "Maven", score: 0 };
export const Npm: Skill = { name: "Npm", score: 0 };
export const CI_CD: Skill = { name: "CI/CD", score: 16 };
export const Maven: Skill = { name: "Maven", score: 3 };
export const Npm: Skill = { name: "Npm", score: 4 };
export const Jenkins: Skill = {
name: "Jenkins",
score: 0,
score: 12,
parent: [CI_CD, DevOps],
};
export const Linux: Skill = { name: "Linux", score: 0 };
export const Linux: Skill = { name: "Linux", score: 23 };
export const Git: Skill = { name: "Git", score: 0 };
export const Svn: Skill = { name: "SVN", score: 0 };
export const Git: Skill = { name: "Git", score: 12 };
export const Svn: Skill = { name: "SVN", score: 1 };
export const Java: Skill = { name: "Java", score: 0 };
export const Java: Skill = { name: "Java", score: 6 };
export const SpringBoot: Skill = {
name: "SpringBoot",
score: 0,
parent: [Java],
};
export const Jee: Skill = { name: "JEE", score: 0, parent: [Java] };
export const Gwt: Skill = { name: "GWT", score: 0, parent: [Java, UX] };
export const Typescript: Skill = { name: "Typescript", score: 0 };
export const Javascript: Skill = { name: "Javascript", score: 0 };
export const Jee: Skill = { name: "JEE", score: 26, parent: [Java] };
export const Gwt: Skill = { name: "GWT", score: 4, parent: [Java, UX] };
export const Typescript: Skill = { name: "Typescript", score: 25 };
export const Javascript: Skill = { name: "Javascript", score: 19 };
export const Angular: Skill = {
name: "Angular",
score: 0,
score: 17,
parent: [Typescript, UX],
};
export const ExtJS: Skill = {
name: "ExtJS",
score: 0,
score: 14,
parent: [Javascript, UX],
};
export const Bash: Skill = { name: "Bash", score: 0 };

Loading…
Cancel
Save