Как обеспечить, чтобы изображение помещалось в контейнер, не перекрывая поле ввода, независимо от его размера

Описание

В настоящее время, если пользователь захватывает изображение и вставляет его, изображение в контейнере img-preview перекрывает userInput. Такое поведение можно явно наблюдать, когда пользователь захватывает и вставляет в чат-бот изображение, высота которого превышает его ширину.

Ожидаемое поведение

Создайте изображение, отображаемое внутри контейнера img-preview-container, обрезав содержимое изображения на границе элемента контейнера, независимо от размера изображения, которое пользователь вставил с помощью клавиш «ctrl» и «v».

Текущее поведение

В настоящее время, если пользователь использует клавишу PrtSc, чтобы захватить определенную часть экрана и вставить ее в поле ввода текста с помощью Ctrl + V, вставленное изображение перекрывает поле ввода текста.

const promptInput = document.getElementById("userInput");
const chatContainer = document.getElementById("chatContainer");
const typingIndicator = document.getElementById("typingIndicator");
const sidebar = document.getElementById("sidebar");
const sidebarContent = document.getElementById("sidebarContent");
const imageContainer = document.getElementById("imageContainer");

async function sendMessage() {
  const prompt = promptInput.value.trim();
  if (!prompt && imageContainer.children.length === 0) {
    alert("Please enter a message or add an image.");  // Browser pop up message
    return;
  }

  // Collect image data
  const images = Array.from(imageContainer.querySelectorAll('.img-preview'))
    .map(img => img.src.split(',')[1]); // Extract base64 data

  addMessage(prompt, 'user', images);
  promptInput.value = "";

  showTypingIndicator();

  const generatedText = await generateText(prompt, images);
  addMessage(generatedText, 'bot');

  hideTypingIndicator();
  //clearImagePreviews(); Add this code if you want the image in the imageContainer disappear if the user sends the image.
}

async function generateText(prompt, images) {
  try {
    const response = await fetch("http://127.0.0.1:5000/generate_text_stream", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ prompt, images }),
    });

    if (!response.ok) {
      console.error("Error:", response.statusText);
      return "Error occurred while generating response.";
    }

    const reader = response.body.getReader();
    const decoder = new TextDecoder();
    let isFinished = false;
    let generatedTextContent = "";

    while (!isFinished) {
      const { done, value } = await reader.read();
      if (done) {
        isFinished = true;
        break;
      }
      generatedTextContent += decoder.decode(value, { stream: true });
    }

    return generatedTextContent;
  } catch (error) {
    console.error("Error:", error);
    return "An error occurred.";
  }
}

function addMessage(text, type, images = []) {
  const messageDiv = document.createElement("div");
  messageDiv.className = `message ${type}`;

  const messageContent = document.createElement("div");
  messageContent.className = "message-bubble fadeIn";
  messageContent.innerHTML = `<p>${text}</p>`;

  images.forEach(src => {
    const img = document.createElement("img");
    img.src = `data:image/png;base64,${src}`;
    img.classList.add("message-image");
    messageContent.appendChild(img);
  });

  messageDiv.appendChild(messageContent);
  chatContainer.appendChild(messageDiv);
  chatContainer.scrollTop = chatContainer.scrollHeight;
}

function clearImagePreviews() {
  while (imageContainer.firstChild) {
    imageContainer.removeChild(imageContainer.firstChild);
  }
  checkImageContainerVisibility();
}

let typingTimeout;

function showTypingIndicator() {
  clearTimeout(typingTimeout);
  typingIndicator.style.display = "inline-block";
}

function hideTypingIndicator() {
  typingTimeout = setTimeout(() => {
    typingIndicator.style.display = "none";
  }, 1000);
}

function handleKeyPress(event) {
  if (event.key === "Enter") {
    sendMessage();
  }
}

function toggleSidebar() {
  if (sidebar.style.width === "500px") {
    sidebar.style.width = "0";
    sidebarContent.style.display = "none";
  } else {
    sidebar.style.width = "500px";
    sidebarContent.style.display = "block";
  }
}

window.onload = () => addMessage("Hello! How can I assist you today?", 'bot');

document.addEventListener('DOMContentLoaded', () => {
  const textInput = document.getElementById('userInput');

  textInput.addEventListener('paste', (event) => {
    const items = (event.clipboardData || window.clipboardData).items;
    for (const item of items) {
      if (item.type.indexOf('image') !== -1) {
        const file = item.getAsFile();
        const reader = new FileReader();
        reader.onload = (event) => {
          displayImage(event.target.result);
        };
        reader.readAsDataURL(file);
        event.preventDefault();
      }
    }
  });

  function displayImage(src) {
    const imgContainer = document.createElement('div');
    imgContainer.classList.add('img-preview-container');

    const img = document.createElement('img');
    img.src = src;
    img.classList.add('img-preview');

    const removeButton = document.createElement('button');
    removeButton.classList.add('remove-button');
    removeButton.textContent = '✖';
    removeButton.addEventListener('click', () => {
      imgContainer.remove();
      checkImageContainerVisibility();
    });

    imgContainer.appendChild(img);
    imgContainer.appendChild(removeButton);
    imageContainer.appendChild(imgContainer);
    checkImageContainerVisibility();

    const all_images = imageContainer.querySelectorAll('.img-preview-container');
    all_images.forEach(img => img.style.width = `${100 / all_images.length - 10}%`);
  }

  function checkImageContainerVisibility() {
    if (imageContainer.children.length > 0) {
      imageContainer.classList.remove('hidden');
    } else {
      imageContainer.classList.add('hidden');
    }
  }

  // Initial check to hide image container if empty
  checkImageContainerVisibility();
});
body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    background-color: #f0f2f5;
    color: #333;
    margin: 0;
    padding: 0;
    text-align: center;
}

h1 {
    color: #333;
    text-align: center;
    margin: 20px 0;
}

/* Generate report button styling */
button, input[type = "submit"] {
    padding: 12px 24px;
    background-color: #007bff;
    color: #fff;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    font-size: 16px;
    transition: background-color 0.3s, transform 0.3s;
}

button:hover, input[type = "submit"]:hover {
    background-color: #0056b3;
    transform: translateY(-2px);
}

/* Form styling */
form {
    background: #fff;
    border-radius: 8px;
    padding: 20px;
    max-width: 600px;
    margin: 20px auto;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}

label {
    display: block;
    margin-bottom: 8px;
    font-weight: 600;
    font-size: 16px;
}

select, input[type = "submit"] {
    width: calc(100% - 22px);
    padding: 12px;
    margin-bottom: 20px;
    border-radius: 6px;
    border: 1px solid #ddd;
    font-size: 16px;
}

select {
    background-color: #f9f9f9;
}

/* Report page styling */
.report-container {
    background: #fff;
    border-radius: 8px;
    padding: 20px;
    max-width: 1000px;
    margin: 20px auto;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
    overflow: auto;
}

a {
    color: #007bff;
    text-decoration: none;
    font-weight: 500;
}

a:hover {
    text-decoration: underline;
}

/* Section that will show report content */
.report-content {
    margin-top: 20px;
}

.report-content iframe {
    width: 100%;
    border: none;
    height: 600px;
}



/* Chat Bot */
.container {
    margin-top: 0;
    width: 90%;
    max-width: 450px;
    margin: 10px auto 0;
    background-color: #fff;
    border-radius: 12px;
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.1);
    padding: 20px;
    transition: all 0.3s;
}

.chat {
    overflow-y: auto;
    height: 400px;
    margin-bottom: 20px;
    border-bottom: 2px solid #e2e2e2;
}

.message {
    display: flex;
    margin-bottom: 12px;
}

.message.user {
    justify-content: flex-end;
}

.message-bubble {
    padding: 12px 18px;
    max-width: 70%;
    border-radius: 20px;
    line-height: 1.6;
    font-size: 0.95rem;
}

.message.user .message-bubble {
    background-color: #3182ce;
    color: white;
}

.message.bot .message-bubble {
    background-color: #e2e2e2;
    color: #333;
}


.message-image {
    max-width: 100px; /* Set the maximum width for the image */
    max-height: 100px; /* Set the maximum height for the image */
    margin: 5px;
    display: inline-block;
    object-fit: cover; /* Ensures the image retains its aspect ratio */
}


input[type = "text"] {
    flex: 1;
    padding: 12px 18px;
    border: 2px solid #e2e2e2;
    border-radius: 8px 0 0 8px;
    font-size: 1rem;
    outline: none;
    color: black;
}

.send-button {
    width: 110px;
    background-color: #3182ce;
    color: white;
    padding: 12px 18px;
    border: none;
    border-radius: 0 8px 8px 0;
    cursor: pointer;
    transition: background-color 0.3s;
}

.send-button:hover {
    background-color: #2c5282;
}

.footer {
    text-align: center;
    padding: 15px 0;
    font-size: 0.9rem;
    color: #666;
    position: static;
    border-top: 1px solid #e2e2e2;
    background-color: #fff;
    position: fixed;
    bottom: 0;
    width: 100%;
}

@keyframes fadeIn {
    from {
        opacity: 0;
    }

    to {
        opacity: 1;
    }
}

.fadeIn {
    animation: fadeIn 1s;
}

@media (max-width: 600px) {
    .container {
        width: 95%;
        margin: 10px auto 0;
    }

    .chat {
        height: 300px;
    }

    .input-container {
        max-width: 95%;
    }

    input[type = "text"],
    .send-button {
        padding: 10px 14px;
        font-size: 0.9rem;
    }

    .footer {
        font-size: 0.8rem;
        margin-top: 30px;
    }
}

.typing-indicator {
    display: none;
    align-items: center;
    justify-content: flex-end;
    margin-top: 8px;
    width: 10px;
    height: 10px;
    background-color: #333;
    border-radius: 50%;
    margin-left: 4px;
    animation: typing 1s infinite;
}

@keyframes typing {
    0%,
    100% {
        transform: scale(1);
        opacity: 1;
    }

    50% {
        transform: scale(1.2);
        opacity: 0.7;
    }
}

/* Side bar */
.sidebar {
    position: fixed;
    top: 10px;
    right: 10px;
    height: 100%;
    width: 100%;
    max-width: 500px;
    background-color: none;
    overflow-x: hidden;
    transition: 0.5s;
    padding-top: 60px;
    color: white;
}

.sidebar-content {
    display: none;
}

.sidebar-content h2 {
    text-align: center;
}

.sidebar-content p {
    padding: 10px;
}

.toggle-button {
    position: fixed;
    top: 10px;
    right: 10px;
    padding: 15px;
    background-color: #3182ce;
    color: white;
    border: none;
    cursor: pointer;
}

.toggle-button:hover {
    background-color: #2c5282;
}

img {
    max-width: 90%;
    margin: 20px 0;
    border: 1px solid #ddd;
    border-radius: 10px;
}

.image-container {
    width: 100%;
    /*padding: 12px 18px;*/


    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    background-color: #fff;
    /*border: 1px solid #444;*/
    min-height: 50px;
    max-height: 70px;
    margin-bottom: 10px;

    border: 2px solid #e2e2e2;
    border-radius: 8px 8px 8px 8px;
}

.hidden {
    display: none;
}

.img-preview-container {
    position: relative;
    display: inline-block;
    max-width: 15%;
}

.img-preview {
    max-width: 100%;
    border-radius: 5px;
}


.remove-button:hover {
    background-color: #45a049;
}

.remove-button {
    position: absolute;
    top: 5px;
    right: 5px;
    background-color: #ff4d4d;
    border: none;
    border-radius: 50%;
    color: white;
    cursor: pointer;
    width: 20px;
    height: 20px;
    font-size: 12px;
    line-height: 20px;
    text-align: center;
    padding: 0;
}
<!DOCTYPE html>
<html lang = "en">
  <head>
    <meta charset = "UTF-8" />
    <meta name = "viewport" content = "width=device-width, initial-scale=1.0" />
    <title>Portfolio Backtesting</title>
    <link
      rel = "stylesheet"
      href = "{{ url_for('static', filename='css/style.css') }}"
    />
  </head>
  <body>
    <h1>Portfolio Backtesting</h1>
    <form method = "post" action = "/">
      <label for = "cs_model">Choose Cross-Sectional Model:</label>
      <select id = "cs_model" name = "cs_model">
        <option value = "EW">Equal Weight (EW)</option>
        <option value = "MSR">Maximum Sharpe Ratio (MSR)</option>
        <option value = "GMV">Global Minimum Variance (GMV)</option>
        <option value = "MDP">Minimum Drawdown Risk (MDP)</option>
        <option value = "EMV">Equal Risk Contribution (EMV)</option>
        <option value = "RP">Risk Parity (RP)</option>
      </select>

      <label for = "ts_model">Choose Time-Series Model:</label>
      <select id = "ts_model" name = "ts_model">
        <option value = "VT">Volatility Targeting (VT)</option>
        <option value = "CVT">Conditional Value at Risk Targeting (CVT)</option>
        <option value = "KL">Kelly Criterion (KL)</option>
        <option value = "CPPI">
          Constant Proportion Portfolio Insurance (CPPI)
        </option>
      </select>

      <input type = "submit" value = "Generate Report" />
    </form>

    <div class = "sidebar" id = "sidebar">
      <button class = "toggle-button" onclick = "toggleSidebar()">☰</button>
      <div class = "sidebar-content" id = "sidebarContent">
        <div class = "container bg-white rounded-lg shadow-md">
          <h1 class = "text-3xl font-bold mb-4 text-center">ChatBot</h1>
          <div class = "chat" id = "chatContainer"></div>
          <div class = "image-container hidden" id = "imageContainer"></div>
          <div class = "flex">
            <input
              type = "text"
              id = "userInput"
              placeholder = "Type your message here..."
              class = "outline-none"
              onkeyup = "handleKeyPress(event)"
            />
            <button class = "send-button" onclick = "sendMessage()">Send</button>
          </div>
          <div class = "typing-indicator" id = "typingIndicator"></div>
        </div>
      </div>
    </div>
    <script src = "{{ url_for('static', filename='js/script.js') }}"></script>
  </body>
</html>

"Сделать изображение, показанное внутри img-preview-container"... Непонятно, как вы хотите, чтобы это произошло: обрезать содержимое изображения на границе элемента-контейнера или сжать всю картинку до тех пор, пока она не поместится в контейнер? Все дело в пропорциональности. Если вы хотите уменьшить его, вы можете закончить тонким вертикальным предметом, если его ширина значительно меньше высоты. Это то, чего ты хочешь?

Congregator 21.07.2024 13:38

Я думал об обрезке содержимого изображения на границе элемента контейнера. Я отредактировал сообщение для более ясной цели.

loupdaniel 21.07.2024 13:44

Попробуйте добавить CSS object-fit: cover или fill к <img>.

zer00ne 21.07.2024 14:05

Я пробовал добавить оба атрибута CSS, соответствующие объекту, но ни один из них не решил проблему.

loupdaniel 21.07.2024 15:16
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
4
57
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Более простой способ сделать это — добавить правило CSS:

.image-container {
    overflow:hidden
}

Таким образом, все лишнее будет скрыто.

Разве это не приводит к исчезновению изображения, если оно перекрывается? Я намеревался показать часть изображения, обрезая содержимое изображения, если оно перекрывается.

loupdaniel 21.07.2024 16:29

@loupdaniel Что именно вы имеете в виду под перекрытием? Я понимаю, что у вас есть контейнер и вы хотите сохранить изображение в исходных границах контейнера. Собственно, та часть изображения, которая не помещается внутрь, «лишний контент» — это то, что «переливается» из контейнера. Именно для этого и предназначено свойство overflow CSS. Когда вы говорите, что .image-container должно overflow:hidden, изображение отображается вплоть до границы контейнера, но не дальше. Я проверил это в приведенном вами фрагменте, и он работал так, как задумано.

Congregator 21.07.2024 20:05

Я неправильно понял ваше решение: изображение буквально исчезнет, ​​если оно переполнится. Протестировал код, работает отлично!

loupdaniel 21.07.2024 20:19

Другие вопросы по теме