JS Custom chat (VueJS)

  • Автор темы Автор темы geneff
  • Дата начала Дата начала

geneff

Middle Developer
Скриптер
Привет, от нечего делать, мне стало интересно как можно реализовать собственный чат в Rage MP. Не много покопавшись на форуме Rage MP я нашел пример от разработчиков дефолтного чата Rage MP, разобрашись в коде, я решил что нужно его переделать на Vue, ведь дефолтый чат написал на JQuery, а как по мне это уже устаревшая библиотека и нужно быть в тренде.

JavaScript:
<template>
  <div>
    <div v-if="showChat" id="chat">
      <ul id="chat_messages">
        <li v-for="message in chatMessages" :key="message">
          {{ message }}
        </li>
      </ul>
      <input v-show="showInput"  v-model="inputText" ref="input" id="chat_msg" type="text" />
    </div>
  </div>

</template>

<script>
export default {
  name: 'RageMPChat',

  keys: {
    KEY_T: 84,
    KEY_ENTER: 13
  },

  data() {
    return {
      showInput: false, // Отвечает за показ ввода текста
      showChat: false, // Отвечает за показ нашего чата
      chatMessages: [], // список сообщений в чате
      inputText: '', // Тут хранится наш текст, который мы вводим в инпут
      active: true // Отвечает за возможность писать в чат
    }
  },

  methods: {
    // Это у нас обработчик нажатия клавиш
    addKeyListener(e) {
        // Если мы нажимаем Т и у нас еще не показан инпут и можно писать в чат
      if (e.which === this.$options.keys.KEY_T && !this.showInput && this.active) {
        // показываем инпут
        this.enableChatInput(true);
        e.preventDefault();
        return;
      }
        // если мы нажали Ентер и у нас открыт инпут
      if (e.which === this.$options.keys.KEY_ENTER && this.showInput) {
        let text = this.inputText;
          // закрываем инпут
        this.enableChatInput(false);

        if (!text.length) {
          return;
        }
          // если это не команда - значит сообщение xD
        if (text.charAt(0) !== "/") {
          mp.invoke("chatMessage", text);
          return;
        }
      
        // тут мы избавляемся от "/"
        text = text.substr(1);

        if (text.length) {
          mp.invoke("command", text);
        }
      }
    },
    // Пушим наше сообщение в чатбокс
    push(text) {
      if (this.chatMessages.length >= 50) {
        this.chatMessages.shift();
      }

      this.chatMessages.unshift(text);
    },
    // очищаем наш список сообщениц
    clear() {
      this.chatMessages.length = 0;
    },
    // показать / убрать интуп ввода текста
    enableChatInput(enable) {
      if (!this.active && enable) {
        return;
      }

      if (enable !== this.showInput) {
        mp.invoke("focus", enable);
        mp.invoke("setTypingInChatState", enable);

        this.showInput = enable;
        this.inputText = '';
        enable && this.$nextTick().then(() => this.$refs.input.focus());
      }
    },
    // включить / отключить возможность писать в чат
    activate(toggle) {
      if (!toggle && this.showInput) {
        this.enableChatInput(false);
      }
      this.active = toggle;
    },
        // показать / скрыть чат
    show(toggle) {
      if (!toggle && this.showInput) {
        this.enableChatInput(false);
      }
      this.showChat = toggle;
    }
  },
  created() {
    if (mp.events) {
      const api = {
        "chat:push": this.push,
        "chat:clear": this.clear,
        "chat:activate": this.activate,
        "chat:show": this.show
      };

        // Тут мы добавляем ивенты на клиент
      for (const fn in api) {
        mp.events.add(fn, api[fn]);
      }
        // Теперь, если я все правильно понял у нас
        // mp.gui.chat.push(text) - выполняет функцию this.push(text) - то-есть добавляет сообщение в чат
        // mp.gui.chat.show(toggle) - this.show(toggle)
        // mp.gui.chat.clear() - this.clear()
        // mp.gui.chat.active(toggle) - this.active(toggle)
    }
  
    // Добавляем наш обработчик в событие
    document.addEventListener("keydown", this.addKeyListener);
  },

  beforeUnmount() {
      // Удалем наш обработчик из события
    document.removeEventListener("keydown", this.addKeyListener);
  },

  mounted() {
      // Включаем чат
    this.showChat = true;
    this.push("Multiplayer started!!!");
  }
}
</script>


// Это дефолт стили чата Rage MP
<style scoped>
*, body, html {
  padding: 0;
  margin: 0
}

#chat, a, body, html {
  color: #fff
}

body, html {
  -webkit-font-smoothing: antialiased;
  overflow: hidden;
  font-size: 14px;
  -webkit-user-select: none
}


#chat {
  width: 800px;
  line-height: 24px;
  font-weight: 700;
  text-shadow: 1px 1px 0 #000, -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
  font-family: "Arial", sans-serif;
  font-size: 16px;
  margin-left: 15px;
}

@media screen and (min-height: 1080px) {
  #chat {
    font-size: 18px !important;
    font-weight: 700;
  }
}

#chat ul#chat_messages {
  height: 285px;
  margin-top: 2vh; /*2vh*/
  transform: rotate(180deg);
  padding: 10px 20px; /* old padding: 10px 20px*/
  list-style-type: none;
  overflow: auto;
}

#chat ul#chat_messages > li {
  transform: rotate(-180deg)
}

#chat input#chat_msg {
  color: #fff;

  background-color: rgba(208,60,55,0.7);
  outline: 0;
  border: none;
  font-family: Myriad Pro, Open Sans, sans-serif;
  font-size: 13px;
  line-height: 35px;
  width: 35vw;
  padding: 5px 5px 5px 15px
}

::-webkit-scrollbar {
  width: 11px;
}

::-webkit-scrollbar-thumb {
  background: rgba(255, 17, 0, 0.3);
  border-radius: 20px

}

::-webkit-scrollbar-thumb:hover {
  background: rgba(255, 17, 0, 0.65)
}
</style>
JavaScript:
module.exports = {
    publicPath: './',
    lintOnSave: process.env.NODE_DEV !== "production"
}
Осталось еще на клиенте указать что это у нас будет чат.
JavaScript:
let customChat = null;
mp.events.add('guiReady', () => {
    if (!customChat) {
        mp.gui.chat.show(false);
        customChat = mp.browsers.new(/*Тут ваш путь к .html*/);
        customChat.markAsChat();
    }
})
Ну и на серверной части:
JavaScript:
mp.events.add('playerChat', (player, text) => {
    player.outputChatBox(`${player.name}: ${text}`);
});

Если у вас будут какие-либо вопросы - задавайте. Постарался все моменты описать, но кто его знает, возможно кому-то этого будет недостаточно. Так что не стесняйтесь и задавайте вопросы.
 
Последнее редактирование:
Давно хотел разобраться как реализовать кастомный чат :) Теперь это будет гораздо проще.
 
Давно хотел разобраться как реализовать кастомный чат :) Теперь это будет гораздо проще.
Я бы закинул свой полностью кастомный чат под РП сервера, но там Vue CLI и RPC, слишком много зависимостей :)
 
Может кто то напишет короткий туториал как это реализовать?! Так сказать на пальцах, без фреймворков))
 
Может кто то напишет короткий туториал как это реализовать?! Так сказать на пальцах, без фреймворков))
Без фреймворка, но с использованием JQuery

HTML:
<html>
<head>
    <link rel="stylesheet" type="text/css" href="style/checkbox.css" media="screen">
    <link rel="stylesheet" type="text/css" href="style/main.css" media="screen">
    <meta charset="utf-8"/>
</head>
<body>
<div id="chat" class="ui_element">
    <ul id="chat_messages">
    </ul>
</div>
<script type="text/javascript" src="jquery-1.11.3.min.js"></script>
<script src="js/main.js"></script>
</body>
</html>
JavaScript:
let chat =
{
    size: 0,
    container: null,
    input: null,
    enabled: false,
    active: true
};

function enableChatInput(enable)
{
    if(chat.active == false
        && enable == true)
        return;
    
    if (enable != (chat.input != null))
    {
        mp.invoke("focus", enable);
        mp.invoke("setTypingInChatState", enable);

        if (enable)
        {
            chat.input = $("#chat").append('<div><input id="chat_msg" type="text" /></div>').children(":last");
            chat.input.children("input").focus();
        }
        else
        {
            chat.input.fadeOut('fast', function()
            {
                chat.input.remove();
                chat.input = null;
            });
        }
    }
}

let idx = 0;

var chatAPI =
{
    push: (text) =>
    {
        chat.container.prepend("<li>" + text + "</li>");

        chat.size++;

        if (chat.size >= 50)
        {
            chat.container.children(":last").remove();
        }
    },
    
    clear: () =>
    {
        chat.container.html("");
    },
    
    activate: (toggle) =>
    {
        if (toggle == false
            && (chat.input != null))
            enableChatInput(false);
            
        chat.active = toggle;
    },
    
    show: (toggle) =>
    {
        if (toggle == false
            && (chat.input != null))
            enableChatInput(false);
            
        if(toggle)
            $("#chat").show();
        else
            $("#chat").hide();
        
        chat.active = toggle;
    }
};

if(mp.events)
{
    let api = {"chat:push": chatAPI.push, "chat:clear": chatAPI.clear, "chat:activate": chatAPI.activate, "chat:show": chatAPI.show};

    for(let fn in api)
    {
        mp.events.add(fn, api[fn]);
    }
}

$(document).ready(function()
{
    chat.container = $("#chat ul#chat_messages");
    
    $(".ui_element").show();

    chatAPI.push("Multiplayer started.");
    
    $("body").keydown(function(event)
    {
        if (event.which == 84 && chat.input == null
            && chat.active == true)
        {
            enableChatInput(true);
            event.preventDefault();
        }
        else if (event.which == 13 && chat.input != null)
        {
            let value = chat.input.children("input").val();

            if (value.length > 0)
            {
                if (value[0] == "/")
                {
                    value = value.substr(1);

                    if (value.length > 0)
                        mp.invoke("command", value);
                }
                else
                {
                    mp.invoke("chatMessage", value);
                }
            }

            enableChatInput(false);
        }
    });
});
CSS:
* {
    margin: 0;
    padding: 0;
}

html, body {
    -webkit-font-smoothing: antialiased;
    overflow: hidden;
    padding: 0;
    margin: 0;
    color: white;
    font-size: 14px;
    -webkit-user-select: none;
}

a {
    text-decoration: none;
    color: white;
}

.ui_element {
    display: none;
    position: absolute;
    width: 100vw;
    height: 100vh;
    z-index: 2;
}

#chat {
    display: block;
    z-index: 0;
    color: white;
    color: rgb(255, 255, 255);
    line-height: 30px;
    font-weight: bold;
    text-shadow: 1px 1px 0 rgb(0, 0, 0), -1px -1px 0 rgb(0, 0, 0), 1px -1px 0 rgb(0, 0, 0), -1px 1px 0 rgb(0, 0, 0), 1px 1px 0 rgb(0, 0, 0);
    font-family: Myriad Pro, Open Sans, sans-serif;
    font-size: 16px;
    letter-spacing: 0.4px;
    margin-left: 15px;
}

@media screen and (min-height: 1080px) {
    #chat {
        font-size: 18px !important;
        font-weight: bold;
    }
}

#chat ul#chat_messages {
    overflow: hidden;
    height: 285px;
    margin-top: 2vh;
    transform: rotate(180deg);
    width: 37vw;
    padding: 10px 20px;
    list-style-type: none;
}

#chat ul#chat_messages > li {
    transform: rotate(-180deg);
}

#chat input#chat_msg {
    color: white;
    background: rgba(0, 0, 0, 0.5);
    outline: none;
    border: none;
    font-family: Myriad Pro, Segoe UI, Verdana, sans-serif;
    font-size: 13px;
    line-height: 35px;
    width: 35vw;
    padding: 5px 5px 5px 15px;
}

#chat_channels li {
    float: left;
    border: 1px solid black;
    list-style-type: none;
    padding: 5px 15px;
    margin: 5px 5px 0px 0px;
    text-transform: uppercase;
    font-size: 13px;
    text-shadow: none;
}

#chat_channels li#active {
    color: rgb(165, 69, 165);
}
JavaScript:
let customChat = null;
mp.events.add('guiReady', () => {
    if (!customChat) {
        mp.gui.chat.show(false);
        customChat = mp.browsers.new(/*Тут ваш путь к .html*/);
        customChat.markAsChat();
    }
})
 
Используется стандартный чат, я его не трогал.
Функция для очистки не существует - mp.gui.chat.clear
На скрине показано что в чате видны команды которые были выполнены, после вызова clear я получил уведомление с ошибкой, потом вывел в чат тип - undefined, затем все ключи которые присутствуют у объекта - ключа clear не существует.
Кто-то сталкивался с подобным и как решить проблему?
Screenshot_2.png

Screenshot_5.png
 
Весьма странно. Функции clear нету в документации, но в апи происходит ее регистрация

Screenshot_6.png
 
Назад
Верх