Страницы

Поиск по вопросам

четверг, 9 января 2020 г.

Как сделать чтоб бекграунд шевелился при вращении телефона в руке?

#java #android


Хочу сделать такую штуку, когда картинка на бэкграунде двигается, когда двигаешь
телефон в руке. Типо такой 3D эффект.

Пример: держишь телефон в руке - картика стоит ровно, чуть повернул телефон по оси
(вправо или влево), и картинка тоже чуть-чуть сдвигается.

У меня плохо получается объяснить, но, надеюсь, получилось.

Как сделать такой эффект? 

Я нашел информацию о паралаксе, но это не совсем то, что мне нужно.

P.S. 
Вот что то похожее есть в примере из ответа @Kamran
http://kamran.site/demos/accelerometer/index-1.php

Смысл в том, что картинка на бэкграунде по ширине немного больше ширины экрана, как
бы у нее есть запас хода влево и вправо. Это и дает возможность движения этой картинки,
только я не могу пока что представить, как это должно работать.
    


Ответы

Ответ 1



Параллакс -- именно то, что тебе нужно. Вот пример того что ты хочешь получить: https://www.youtube.com/watch?v=cJ7F5vpk6OU Да и тот пример который ты показал в линке -- это ТОЖЕ ПАРАЛАКС. Как действует внутри сам эфект паралакса: Отрисовываешь все нужные картинки "слоями". Пишешь некоторую функцию для смещения каждого из слоев. Чем дальше находится картинка(условно, "дальше") -- тем медленнее должна она двигаться. В общем-то функция должна быть одна, только изменяться размер смещения должен по коэфициэнту. Привязываешь все функции к значению акселерометра/гироскопа телефона и изменяешь при каждой следующей отрисовке кадра. Готовой формулы нет -- нужно экспериментировать в зависимости от того что ты хочешь получить: Может быть только горизонтальный паралакс-эфект Или по двум осям (вверх и вниз) Или же по всем осям -- в т.ч. приближение и отдаление при опускании и поднимании телефона). Гуглится по запросу "java parallax scrolling accelerometer". Есть пример библиотеки которая это делает, как раз, на джаве: https://github.com/nvanbenschoten/motion При чем сразу с апликухой-примером которую можно поставить на мобильный и проверить вживую: https://github.com/nvanbenschoten/motion/releases/download/v1.1.2/motion-sample.apk В самой апликухе внизу ползунок перетащи вправо до упора что бы лучше было видно сам эфект. UPD: Вот еще одна паралакс библиотека на джаве: https://github.com/SchibstedSpain/Parallax-Layer-Layout И еще одна тоже на джаве: https://github.com/tvbarthel/ParallaxSampleGitHub И пример: https://www.youtube.com/watch?v=KyNBZLzWxYI У тебя есть выбор! Вот пример кода из библиотеки приведенной выше attrs ParallaxImageView public class ParallaxImageView extends android.support.v7.widget.AppCompatImageView implements SensorEventListener { private static final String TAG = ParallaxImageView.class.getName(); /** * If the x and y axis' intensities are scaled to the image's aspect ratio (true) or * equal to the smaller of the axis' intensities (false). If true, the image will be able to * translate up to it's view bounds, independent of aspect ratio. If not true, * the image will limit it's translation equally so that motion in either axis results * in proportional translation. */ private boolean mScaledIntensities = false; /** * The intensity of the parallax effect, giving the perspective of depth. */ private float mParallaxIntensity = 1.2f; /** * The maximum percentage of offset translation that the image can move for each * sensor input. Set to a negative number to disable. */ private float mMaximumJump = .1f; // Instance variables used during matrix manipulation. private SensorInterpreter mSensorInterpreter; private SensorManager mSensorManager; private Matrix mTranslationMatrix; private float mXTranslation; private float mYTranslation; private float mXOffset; private float mYOffset; public ParallaxImageView(Context context) { this(context, null); } public ParallaxImageView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ParallaxImageView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // Instantiate future objects mTranslationMatrix = new Matrix(); mSensorInterpreter = new SensorInterpreter(); // Sets scale type setScaleType(ScaleType.MATRIX); // Set available attributes if (attrs != null) { final TypedArray customAttrs = context.obtainStyledAttributes(attrs, R.styleable.ParallaxImageView); if (customAttrs != null) { if (customAttrs.hasValue(R.styleable.ParallaxImageView_motionIntensity)) { setParallaxIntensity( customAttrs.getFloat(R.styleable.ParallaxImageView_motionIntensity, mParallaxIntensity)); } if (customAttrs.hasValue(R.styleable.ParallaxImageView_motionScaledIntensity)) { setScaledIntensities(customAttrs.getBoolean(R.styleable.ParallaxImageView_motionScaledIntensity, mScaledIntensities)); } if (customAttrs.hasValue(R.styleable.ParallaxImageView_motionTiltSensitivity)) { setTiltSensitivity(customAttrs.getFloat(R.styleable.ParallaxImageView_motionTiltSensitivity, mSensorInterpreter.getTiltSensitivity())); } customAttrs.recycle(); } } // Configure matrix as early as possible by posting to MessageQueue post(this::configureMatrix); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); configureMatrix(); } @Override public void onSensorChanged(SensorEvent event) { if (mSensorInterpreter == null) { return; } final float[] vectors = mSensorInterpreter.interpretSensorEvent(getContext(), event); // Return if interpretation of data failed if (vectors == null) { return; } // Set translation on ImageView matrix setTranslate(vectors[2], -vectors[1]); } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } /** * Registers a sensor manager with the parallax ImageView. Should be called in onResume * or onStart lifecycle callbacks from an Activity or Fragment. */ public void registerSensorManager() { registerSensorManager(SensorManager.SENSOR_DELAY_FASTEST); } /** * Registers a sensor manager with the parallax ImageView. Should be called in onResume * or onStart lifecycle callbacks from an Activity or Fragment. * * @param samplingPeriodUs the sensor sampling period rate */ public void registerSensorManager(int samplingPeriodUs) { if (getContext() == null || mSensorManager != null) { return; } // Acquires a sensor manager mSensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE); if (mSensorManager != null) { mSensorManager.registerListener(this, mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR), samplingPeriodUs); } } /** * Unregisters the ParallaxImageView's SensorManager. Should be called in onPause or onStop * lifecycle callbacks from an Activity or Fragment to avoid leaking sensor usage. */ public void unregisterSensorManager() { unregisterSensorManager(false); } /** * Unregisters the ParallaxImageView's SensorManager. Should be called in onPause from * an Activity or Fragment to avoid continuing sensor usage. * * @param resetTranslation if the image translation should be reset to the origin */ public void unregisterSensorManager(boolean resetTranslation) { if (mSensorManager == null || mSensorInterpreter == null) { return; } mSensorManager.unregisterListener(this); mSensorManager = null; mSensorInterpreter.reset(); if (resetTranslation) { setTranslate(0, 0); } } /** * Sets the intensity of the parallax effect. The stronger the effect, the more distance * the image will have to move around. * * @param parallaxIntensity the new intensity */ public void setParallaxIntensity(float parallaxIntensity) { if (parallaxIntensity < 1) { throw new IllegalArgumentException("Parallax effect must have a intensity of 1.0 or greater"); } mParallaxIntensity = parallaxIntensity; configureMatrix(); } /** * Sets the parallax tilt sensitivity for the image view. The stronger the sensitivity, * the more a given tilt will adjust the image and the smaller needed tilt to reach the * image bounds. * * @param sensitivity the new tilt sensitivity */ public void setTiltSensitivity(float sensitivity) { mSensorInterpreter.setTiltSensitivity(sensitivity); } /** * Sets whether translation should be limited to the image's bounds or should be limited * to the smaller of the two axis' translation limits. * * @param scaledIntensities the scaledIntensities flag */ public void setScaledIntensities(boolean scaledIntensities) { mScaledIntensities = scaledIntensities; } /** * Sets the maximum percentage of the image that image matrix is allowed to translate * for each sensor reading. * * @param maximumJump the new maximum jump */ public void setMaximumJump(float maximumJump) { mMaximumJump = maximumJump; } /** * Sets the image view's translation coordinates. These values must be between -1 and 1, * representing the transaction percentage from the center. * * @param x the horizontal translation * @param y the vertical translation */ private void setTranslate(float x, float y) { if (Math.abs(x) > 1 || Math.abs(y) > 1) { throw new IllegalArgumentException("Parallax effect cannot translate more than 100% of its off-screen size"); } float xScale, yScale; if (mScaledIntensities) { // Set both scales to their offset values xScale = mXOffset; yScale = mYOffset; } else { // Set both scales to the max offset (should be negative, so smaller absolute value) xScale = Math.max(mXOffset, mYOffset); yScale = Math.max(mXOffset, mYOffset); } // Make sure below maximum jump limit if (mMaximumJump > 0) { // Limit x jump if (x - mXTranslation / xScale > mMaximumJump) { x = mXTranslation / xScale + mMaximumJump; } else if (x - mXTranslation / xScale < -mMaximumJump) { x = mXTranslation / xScale - mMaximumJump; } // Limit y jump if (y - mYTranslation / yScale > mMaximumJump) { y = mYTranslation / yScale + mMaximumJump; } else if (y - mYTranslation / yScale < -mMaximumJump) { y = mYTranslation / yScale - mMaximumJump; } } mXTranslation = x * xScale; mYTranslation = y * yScale; configureMatrix(); } /** * Configures the ImageView's imageMatrix to allow for movement of the * source image. */ private void configureMatrix() { if (getDrawable() == null || getWidth() == 0 || getHeight() == 0) { return; } int dWidth = getDrawable().getIntrinsicWidth(); int dHeight = getDrawable().getIntrinsicHeight(); int vWidth = getWidth(); int vHeight = getHeight(); float scale; float dx, dy; if (dWidth * vHeight > vWidth * dHeight) { scale = (float) vHeight / (float) dHeight; mXOffset = (vWidth - dWidth * scale * mParallaxIntensity) * 0.5f; mYOffset = (vHeight - dHeight * scale * mParallaxIntensity) * 0.5f; } else { scale = (float) vWidth / (float) dWidth; mXOffset = (vWidth - dWidth * scale * mParallaxIntensity) * 0.5f; mYOffset = (vHeight - dHeight * scale * mParallaxIntensity) * 0.5f; } dx = mXOffset + mXTranslation; dy = mYOffset + mYTranslation; mTranslationMatrix.set(getImageMatrix()); mTranslationMatrix.setScale(mParallaxIntensity * scale, mParallaxIntensity * scale); mTranslationMatrix.postTranslate(dx, dy); setImageMatrix(mTranslationMatrix); } } SensorInterpreter class SensorInterpreter { private static final String TAG = SensorInterpreter.class.getName(); /** * The standardized tilt vector corresponding to yaw, pitch, and roll deltas from target matrix. */ private float[] mTiltVector = new float[3]; /** * Whether the SensorInterpreter has set a target to calculate tilt offset from. */ private boolean mTargeted = false; /** * The target rotation matrix to calculate tilt offset from. */ private float[] mTargetMatrix = new float[16]; /** * Rotation matrices used during calculation. */ private float[] mRotationMatrix = new float[16]; private float[] mOrientedRotationMatrix = new float[16]; /** * Holds a shortened version of the rotation vector for compatibility purposes. */ private float[] mTruncatedRotationVector; /** * The sensitivity the parallax effect has towards tilting. */ private float mTiltSensitivity = 2.0f; /** * Converts sensor data in a {@link SensorEvent} to yaw, pitch, and roll. * * @param context the context of the * @param event the event to interpret * * @return an interpreted vector of yaw, pitch, and roll delta values */ @SuppressWarnings("SuspiciousNameCombination") public float[] interpretSensorEvent(@NonNull Context context, @Nullable SensorEvent event) { if (event == null) { return null; } // Retrieves the RotationVector from SensorEvent float[] rotationVector = getRotationVectorFromSensorEvent(event); // Set target rotation if none has been set if (!mTargeted) { setTargetVector(rotationVector); return null; } // Get rotation matrix from event's values SensorManager.getRotationMatrixFromVector(mRotationMatrix, rotationVector); // Acquire rotation of screen final int rotation = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation(); // Calculate angle differential between target and current orientation if (rotation == Surface.ROTATION_0) { SensorManager.getAngleChange(mTiltVector, mRotationMatrix, mTargetMatrix); } else { // Adjust axes on screen orientation by remapping coordinates switch (rotation) { case Surface.ROTATION_90: SensorManager.remapCoordinateSystem(mRotationMatrix, AXIS_Y, AXIS_MINUS_X, mOrientedRotationMatrix); break; case Surface.ROTATION_180: SensorManager.remapCoordinateSystem(mRotationMatrix, AXIS_MINUS_X, AXIS_MINUS_Y, mOrientedRotationMatrix); break; case Surface.ROTATION_270: SensorManager.remapCoordinateSystem(mRotationMatrix, AXIS_MINUS_Y, AXIS_X, mOrientedRotationMatrix); break; } SensorManager.getAngleChange(mTiltVector, mOrientedRotationMatrix, mTargetMatrix); } // Perform value scaling and clamping on value array for (int i = 0 ; i < mTiltVector.length ; i++) { // Map domain of tilt vector from radian (-PI, PI) to fraction (-1, 1) mTiltVector[i] /= Math.PI; // Adjust for tilt sensitivity mTiltVector[i] *= mTiltSensitivity; // Clamp values to image bounds if (mTiltVector[i] > 1) { mTiltVector[i] = 1f; } else if (mTiltVector[i] < -1) { mTiltVector[i] = -1f; } } return mTiltVector; } /** * Pulls out the rotation vector from a {@link SensorEvent}, with a maximum length * vector of four elements to avoid potential compatibility issues. * * @param event the sensor event * * @return the events rotation vector, potentially truncated */ @NonNull @VisibleForTesting float[] getRotationVectorFromSensorEvent(@NonNull SensorEvent event) { if (event.values.length > 4) { // On some Samsung devices SensorManager.getRotationMatrixFromVector // appears to throw an exception if rotation vector has length > 4. // For the purposes of this class the first 4 values of the // rotation vector are sufficient (see crbug.com/335298 for details). if (mTruncatedRotationVector == null) { mTruncatedRotationVector = new float[4]; } System.arraycopy(event.values, 0, mTruncatedRotationVector, 0, 4); return mTruncatedRotationVector; } else { return event.values; } } /** * Sets the target direction used for angle deltas to determine tilt. * * @param values a rotation vector (presumably from a ROTATION_VECTOR sensor) */ protected void setTargetVector(float[] values) { SensorManager.getRotationMatrixFromVector(mTargetMatrix, values); mTargeted = true; } /** * Resets the state of the SensorInterpreter, removing any target direction used for angle * deltas to determine tilt. */ public void reset() { mTargeted = false; } /** * Determines the tilt sensitivity of the SensorInterpreter. * * @return the tilt sensitivity */ public float getTiltSensitivity() { return mTiltSensitivity; } /** * Sets the new sensitivity that the SensorInterpreter will scale tilt calculations by. If this * sensitivity is above 1, the interpreter will have to clamp percentages to 100% and -100% at * the tilt extremes. * * @param tiltSensitivity the new tilt sensitivity */ public void setTiltSensitivity(float tiltSensitivity) { if (tiltSensitivity <= 0) { throw new IllegalArgumentException("Tilt sensitivity must be positive"); } mTiltSensitivity = tiltSensitivity; } } И потом в коде mScreenBackground.setImageResource(iRes); и там где вам нужно (например в onResume()) вызываем mScreenBackground.registerSensorManager(); и не забываем отписаться (например в onStop())

Ответ 2



Код гибкий, поработайте со значениями оси... (accY и accX) в основном все что есть для контродя датчиков window.ondevicemotion = function(event) { accX = Math.round(event.accelerationIncludingGravity.x*10) / 10; accY = Math.round(event.accelerationIncludingGravity.y*10) / 10; movement = 10; xA = -(accX / 10) * movement; yA = -(accY / 10) * movement; run(); } вы смотрели пример - http://kamran.site/demos/accelerometer/index-1.php ?

Ответ 3



Пример: JS --- $(document).ready(function() { var has_touch = 'ontouchstart' in document.documentElement; var accX, accY, width, height, xA, yA, movement; if(has_touch || screen.width <= 699) { (resize = function() { height = $(window).height(); width = $(window).width(); $('#container').width(width).height(height); })(); window.ondevicemotion = function(event) { accX = Math.round(event.accelerationIncludingGravity.x*10) / 10; accY = Math.round(event.accelerationIncludingGravity.y*10) / 10; movement = 10; xA = -(accX / 10) * movement; yA = -(accY / 10) * movement; run(); } } else { $('.content').show(); $('#container').addClass('fullscreen').mousemove(function(e) { width = $(this).width(); height = $(this).height(); accX = (((e.pageX - this.offsetLeft)*2 / width) * 10) - 10; accY = (((e.pageY - this.offsetTop)*2 / width) * 10) - 10; xA = -(accX / 10); yA = -(accY / 10); movement = 2; run(); }); } function run() { $('.box').css({'left' : xA+'px', 'top' : yA+'px', 'box-shadow' : ''+xA+'px '+yA+'px 10px rgba(0,0,0,0.3)'}); bX = -(xA*2)-100; bY = -(yA*2)-300; $('#container').css({'background-position' : bX+'px '+bY+'px'}); } }); html --- Accelerometer
тут контент
Пример: http://kamran.site/demos/accelerometer/index-1.php Код: http://kamran.site/demos/accelerometer/download.zip

Комментариев нет:

Отправить комментарий